| #!/usr/bin/env python3 |
| # Copyright 2022 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Rebuilds baseline files by deleting the xml and re-building lint targets.""" |
| |
| import argparse |
| import logging |
| import pathlib |
| import subprocess |
| import sys |
| from typing import List, Optional |
| |
| _SRC_PATH = pathlib.Path(__file__).resolve().parents[3] |
| _CLANK_PATH = _SRC_PATH / 'clank' |
| _OUTPUT_DIR_ROOT = _SRC_PATH / 'out' |
| _NINJA_PATH = _SRC_PATH / 'third_party' / 'ninja' / 'ninja' |
| _GN_PATH = _SRC_PATH / 'buildtools' / 'linux64' / 'gn' |
| |
| sys.path.insert(1, str(_SRC_PATH / 'build')) |
| import gn_helpers |
| |
| |
| def build_all_lint_targets( |
| out_dir: pathlib.Path, |
| args_list: List[str], |
| *, |
| verbose: bool, |
| built_targets: Optional[List[str]] = None) -> List[str]: |
| logging.info(f'Create output directory: {out_dir}') |
| out_dir.mkdir(parents=True, exist_ok=True) |
| |
| args_gn_path = out_dir / 'args.gn' |
| args_gn_content = '\n'.join(args_list) |
| logging.info(f'Populating {args_gn_path}:\n{args_gn_content}') |
| with open(args_gn_path, 'w') as f: |
| f.write(args_gn_content) |
| |
| logging.info('Run `gn gen`.') |
| subprocess.run([_GN_PATH, 'gen', out_dir], check=True) |
| |
| if built_targets is None: |
| built_targets = [] |
| |
| logging.info('Finding all lint targets.') |
| target_names = [] |
| output = subprocess.run([_NINJA_PATH, '-C', out_dir, '-t', 'targets'], |
| check=True, |
| text=True, |
| capture_output=True).stdout |
| for line in output.splitlines(): |
| ninja_target = line.rsplit(':', 1)[0] |
| # Ensure only full-path targets are used (path:name) instead of the |
| # shorthand (name). |
| if ':' in ninja_target and ninja_target.endswith('__lint'): |
| if ninja_target in built_targets: |
| logging.info( |
| f'> Skipping {ninja_target} since it was already built.') |
| continue |
| logging.info(f'> Found {ninja_target}') |
| target_names.append(ninja_target) |
| |
| if not target_names: |
| logging.info('Did not find any targets to build.') |
| else: |
| logging.info(f'Re-building lint targets: {target_names}') |
| cmd = gn_helpers.CreateBuildCommand(str(out_dir)) + target_names |
| # Do not show output by default since all lint warnings are printed. |
| subprocess.run(cmd, check=True, capture_output=not verbose) |
| |
| return built_targets + target_names |
| |
| |
| def _remove_redundant_lint_error(path: pathlib.Path): |
| with open(path) as f: |
| original_content = f.read() |
| |
| # Lint error is always the first issue. |
| first_issue_idx = original_content.find(' <issue\n') |
| if first_issue_idx == -1: |
| return |
| |
| lint_error_idx = original_content.find('id="LintError"', first_issue_idx) |
| if lint_error_idx == -1: |
| return |
| |
| # If the lint error isn't the first issue, we should keep it. |
| if lint_error_idx - first_issue_idx > 20: |
| return |
| |
| issue_end_idx = original_content.find('</issue>\n', lint_error_idx) |
| if issue_end_idx == -1: |
| return |
| |
| # Replace 10 characters past the start of </issue> to remove extra newlines. |
| replaced_content = original_content[:first_issue_idx] + original_content[ |
| issue_end_idx + 10:] |
| |
| with open(path, 'w') as f: |
| f.write(replaced_content) |
| |
| |
| def main(): |
| logging.basicConfig( |
| level=logging.INFO, |
| format='%(levelname).1s %(relativeCreated)7d %(message)s') |
| |
| parser = argparse.ArgumentParser( |
| description=__doc__, formatter_class=argparse.RawTextHelpFormatter) |
| parser.add_argument('-v', |
| '--verbose', |
| action='store_true', |
| help='Used to show ninja output.') |
| parser.add_argument('-q', |
| '--quiet', |
| action='store_true', |
| help='Used to print only warnings and errors.') |
| args = parser.parse_args() |
| |
| if args.quiet: |
| level = logging.WARNING |
| elif args.verbose: |
| level = logging.DEBUG |
| else: |
| level = logging.INFO |
| logging.basicConfig( |
| level=level, format='%(levelname).1s %(relativeCreated)7d %(message)s') |
| |
| include_clank = _CLANK_PATH.exists() |
| |
| git_roots = [_SRC_PATH] |
| if include_clank: |
| git_roots.append(_CLANK_PATH) |
| |
| logging.info(f'Removing "lint-baseline.xml" files under {git_roots}.') |
| |
| repo_file_paths = [] |
| for git_root in git_roots: |
| git_file_paths = subprocess.run( |
| ['git', '-C', str(git_root), 'ls-files'], |
| check=True, |
| capture_output=True, |
| text=True).stdout.splitlines() |
| repo_file_paths.extend((git_root, p) for p in git_file_paths) |
| |
| for git_root, repo_path in repo_file_paths: |
| path = git_root / repo_path |
| if path.name == 'lint-baseline.xml' and path.exists(): |
| logging.info(f'> Deleting: {path}') |
| path.unlink() |
| |
| out_dir = _OUTPUT_DIR_ROOT / 'Lint-Default' |
| gn_args = [ |
| 'use_goma=true', |
| 'target_os="android"', |
| 'treat_warnings_as_errors=false', |
| 'is_component_build=false', |
| 'enable_chrome_android_internal=false', |
| ] |
| built_targets = build_all_lint_targets(out_dir, |
| gn_args, |
| verbose=args.verbose) |
| |
| if include_clank: |
| out_dir = _OUTPUT_DIR_ROOT / 'Lint-Clank' |
| gn_args = [ |
| 'use_goma=true', |
| 'target_os="android"', |
| 'treat_warnings_as_errors=false', |
| 'is_component_build=false', |
| ] |
| built_targets = build_all_lint_targets(out_dir, |
| gn_args, |
| verbose=args.verbose, |
| built_targets=built_targets) |
| |
| out_dir = _OUTPUT_DIR_ROOT / 'Lint-Cast' |
| gn_args = [ |
| 'use_goma=true', |
| 'target_os="android"', |
| 'treat_warnings_as_errors=false', |
| 'is_component_build=false', |
| 'enable_chrome_android_internal=false', |
| 'is_cast_android=true', |
| 'enable_cast_receiver=true', |
| ] |
| built_targets = build_all_lint_targets(out_dir, |
| gn_args, |
| verbose=args.verbose, |
| built_targets=built_targets) |
| |
| out_dir = _OUTPUT_DIR_ROOT / 'Lint-Cronet' |
| gn_args = [ |
| 'use_goma=true', |
| 'target_os="android"', |
| 'treat_warnings_as_errors=false', |
| 'is_component_build=false', |
| 'is_cronet_build = true', |
| ] |
| built_targets = build_all_lint_targets(out_dir, |
| gn_args, |
| verbose=args.verbose, |
| built_targets=built_targets) |
| |
| logging.info('Cleaning up new lint-baseline.xml files.') |
| for git_root, repo_path in repo_file_paths: |
| path = git_root / repo_path |
| if path.name == 'lint-baseline.xml': |
| logging.info(f'> Removing redundant LintError: {path}') |
| _remove_redundant_lint_error(path) |
| |
| |
| if __name__ == '__main__': |
| main() |