blob: 36af0435b1ff71510d5a046be5e1e8f3ec980b7d [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import functools
import logging
import multiprocessing
import os
import pathlib
import subprocess
import sys
from typing import Optional
import json_gn_editor
import utils
_TOOLS_ANDROID_PATH = pathlib.Path(__file__).parents[2].resolve()
if str(_TOOLS_ANDROID_PATH) not in sys.path:
sys.path.append(str(_TOOLS_ANDROID_PATH))
from python_utils import git_metadata_utils
_GIT_IGNORE_STR = '(git ignored file) '
def _split_dep(existing_dep: str, new_dep: str, root: pathlib.Path,
filepath: str, dryrun: bool) -> str:
with json_gn_editor.BuildFile(filepath, root, dryrun=dryrun) as build_file:
if build_file.split_dep(existing_dep, new_dep):
ignore_string = ''
if utils.is_git_ignored(root, filepath):
ignore_string = _GIT_IGNORE_STR
dryrun_string = '[DRYRUN] ' if dryrun else ''
relpath = os.path.relpath(filepath, start=root)
return f'{dryrun_string}Updated {ignore_string}' + relpath
return ''
def main():
parser = argparse.ArgumentParser(
description='Add a new dep to all build targets that depend on an '
'existing dep.')
parser.add_argument('existing_dep', help='The dep to split from.')
parser.add_argument('new_dep', help='The new dep to add.')
parser.add_argument(
'-n',
'--dryrun',
action='store_true',
help='Show which files would be updated but avoid changing them.')
parser.add_argument(
'-v',
'--verbose',
action='count',
default=0,
help='1 to print logging, 2 to print each build file checked.')
args = parser.parse_args()
if args.verbose >= 2:
level = logging.DEBUG
elif args.verbose == 1:
level = logging.INFO
else:
level = logging.WARNING
logging.basicConfig(
level=level, format='%(levelname).1s %(relativeCreated)6d %(message)s')
build_filepaths = []
root = git_metadata_utils.get_chromium_src_path()
logging.info('Finding build files under %s', root)
for dirpath, dirnames, filenames in os.walk(root):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
if utils.is_bad_gn_file(filepath):
continue
if filename.endswith('.gn') or filename.endswith('.gni'):
build_filepaths.append(filepath)
build_filepaths.sort()
num_total = len(build_filepaths)
logging.info('Found %d build files.', num_total)
updated_files = []
ignored_files = []
with multiprocessing.Pool() as pool:
tasks = {
filepath: pool.apply_async(
_split_dep,
(args.existing_dep, args.new_dep, root, filepath, args.dryrun))
for filepath in build_filepaths
}
for idx, filepath in enumerate(tasks.keys()):
logging.debug('[%d/%d] Checking %s', idx, num_total, filepath)
return_value = tasks[filepath].get()
if return_value:
logging.info(return_value)
updated_files.append(filepath)
if _GIT_IGNORE_STR in return_value:
ignored_files.append(filepath)
num_updated = len(updated_files)
num_ignored = len(ignored_files)
print(f'Checked {num_total} and updated {num_updated} build files, '
f'{num_ignored} of which are ignored by git under {root}')
if num_ignored:
print(f'\nThe following {num_ignored} files were ignored by git and '
'may need separate CLs in their respective repositories:')
for filepath in ignored_files:
print(' ' + os.path.relpath(filepath, start=root))
if __name__ == '__main__':
main()