Merge "Extend the update.py script to determine usage." am: 8aedba5144
Original change: https://android-review.googlesource.com/c/platform/development/+/1560028 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ibc6587d2e7d9882a1ce7f6e084885ff4930556f2
This commit is contained in:
committed by
Automerger Merge Worker
commit
a15eb7ee99
@@ -285,6 +285,198 @@ def gen_bp_files(image, install_dir, snapshot_version):
|
|||||||
f.write(androidbp)
|
f.write(androidbp)
|
||||||
|
|
||||||
|
|
||||||
|
def find_all_installed_files(install_dir):
|
||||||
|
installed_files = dict()
|
||||||
|
for root, _, files in os.walk(install_dir, followlinks = True):
|
||||||
|
for file_name in sorted(files):
|
||||||
|
if file_name.endswith('.json'):
|
||||||
|
continue
|
||||||
|
if file_name.endswith('Android.bp'):
|
||||||
|
continue
|
||||||
|
full_path = os.path.join(root, file_name)
|
||||||
|
size = os.stat(full_path).st_size
|
||||||
|
installed_files[full_path] = size
|
||||||
|
|
||||||
|
logging.debug('')
|
||||||
|
for f in sorted(installed_files.keys()):
|
||||||
|
logging.debug(f)
|
||||||
|
logging.debug('')
|
||||||
|
logging.debug('found {} installed files'.format(len(installed_files)))
|
||||||
|
logging.debug('')
|
||||||
|
return installed_files
|
||||||
|
|
||||||
|
|
||||||
|
def find_files_in_props(target_arch, arch_install_dir, variation, name, props, file_to_info):
|
||||||
|
logging.debug('{} {} {} {} {}'.format(
|
||||||
|
target_arch, arch_install_dir, variation, name, props))
|
||||||
|
|
||||||
|
def add_info(file, name, variation, arch, is_cfi, is_header):
|
||||||
|
info = (name, variation, arch, is_cfi, is_header)
|
||||||
|
info_list = file_to_info.get(file)
|
||||||
|
if not info_list:
|
||||||
|
info_list = []
|
||||||
|
file_to_info[file] = info_list
|
||||||
|
info_list.append(info)
|
||||||
|
|
||||||
|
def find_file_in_list(dict, key, is_cfi):
|
||||||
|
list = dict.get(key)
|
||||||
|
logging.debug(' {} {}'.format(key, list))
|
||||||
|
if list:
|
||||||
|
for item in list:
|
||||||
|
item_path = os.path.join(arch_install_dir, item)
|
||||||
|
add_info(item_path, name, variation, arch, is_cfi, False)
|
||||||
|
|
||||||
|
def find_file_in_dirs(dict, key, is_cfi, is_header):
|
||||||
|
dirs = dict.get(key)
|
||||||
|
logging.debug(' {} {}'.format(key, dirs))
|
||||||
|
if dirs:
|
||||||
|
for dir in dirs:
|
||||||
|
dir_path = os.path.join(arch_install_dir, dir)
|
||||||
|
logging.debug(' scanning {}'.format(dir_path))
|
||||||
|
for root, _, files in os.walk(dir_path, followlinks = True):
|
||||||
|
for file_name in sorted(files):
|
||||||
|
item_path = os.path.join(root, file_name)
|
||||||
|
add_info(item_path, name, variation, arch, is_cfi, is_header)
|
||||||
|
|
||||||
|
def find_file_in_dict(dict, is_cfi):
|
||||||
|
logging.debug(' arch {}'.format(arch))
|
||||||
|
logging.debug(' name {}'.format( name))
|
||||||
|
logging.debug(' is_cfi {}'.format(is_cfi))
|
||||||
|
|
||||||
|
src = dict.get('src')
|
||||||
|
logging.debug(' src {}'.format(src))
|
||||||
|
if src:
|
||||||
|
src_path = os.path.join(arch_install_dir, src)
|
||||||
|
add_info(src_path, name, variation, arch, is_cfi, False)
|
||||||
|
|
||||||
|
notice = dict.get('notice')
|
||||||
|
logging.debug(' notice {}'.format(notice))
|
||||||
|
if notice:
|
||||||
|
notice_path = os.path.join(arch_install_dir, notice)
|
||||||
|
add_info(notice_path, name, variation, arch, is_cfi, False)
|
||||||
|
|
||||||
|
find_file_in_list(dict, 'init_rc', is_cfi)
|
||||||
|
find_file_in_list(dict, 'vintf_fragments', is_cfi)
|
||||||
|
|
||||||
|
find_file_in_dirs(dict, 'export_include_dirs', is_cfi, True)
|
||||||
|
find_file_in_dirs(dict, 'export_system_include_dirs', is_cfi, True)
|
||||||
|
|
||||||
|
for arch in sorted(props):
|
||||||
|
name = props[arch]['name']
|
||||||
|
find_file_in_dict(props[arch], False)
|
||||||
|
cfi = props[arch].get('cfi')
|
||||||
|
if cfi:
|
||||||
|
find_file_in_dict(cfi, True)
|
||||||
|
|
||||||
|
|
||||||
|
def find_all_props_files(install_dir):
|
||||||
|
|
||||||
|
# This function builds a database of filename to module. This means that we
|
||||||
|
# need to dive into the json to find the files that the vendor snapshot
|
||||||
|
# provides, and link these back to modules that provide them.
|
||||||
|
|
||||||
|
file_to_info = dict()
|
||||||
|
|
||||||
|
props = build_props(install_dir)
|
||||||
|
for target_arch in sorted(props):
|
||||||
|
arch_install_dir = os.path.join(install_dir, target_arch)
|
||||||
|
for variation in sorted(props[target_arch]):
|
||||||
|
for name in sorted(props[target_arch][variation]):
|
||||||
|
find_files_in_props(
|
||||||
|
target_arch,
|
||||||
|
arch_install_dir,
|
||||||
|
variation,
|
||||||
|
name,
|
||||||
|
props[target_arch][variation][name],
|
||||||
|
file_to_info)
|
||||||
|
|
||||||
|
logging.debug('')
|
||||||
|
for f in sorted(file_to_info.keys()):
|
||||||
|
logging.debug(f)
|
||||||
|
logging.debug('')
|
||||||
|
logging.debug('found {} props files'.format(len(file_to_info)))
|
||||||
|
logging.debug('')
|
||||||
|
return file_to_info
|
||||||
|
|
||||||
|
|
||||||
|
def get_ninja_inputs(ninja_binary, ninja_build_file, modules):
|
||||||
|
"""Returns the set of input file path strings for the given modules.
|
||||||
|
|
||||||
|
Uses the `ninja -t inputs` tool.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ninja_binary: The path to a ninja binary.
|
||||||
|
ninja_build_file: The path to a .ninja file from a build.
|
||||||
|
modules: The list of modules to scan for inputs.
|
||||||
|
"""
|
||||||
|
inputs = set()
|
||||||
|
cmd = [
|
||||||
|
ninja_binary,
|
||||||
|
"-f",
|
||||||
|
ninja_build_file,
|
||||||
|
"-t",
|
||||||
|
"inputs",
|
||||||
|
"-d",
|
||||||
|
] + list(modules)
|
||||||
|
logging.debug('invoke ninja {}'.format(cmd))
|
||||||
|
inputs = inputs.union(set(
|
||||||
|
subprocess.check_output(cmd).decode().strip("\n").split("\n")))
|
||||||
|
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
|
||||||
|
def check_module_usage(install_dir, ninja_binary, ninja_file, goals, output):
|
||||||
|
all_installed_files = find_all_installed_files(install_dir)
|
||||||
|
all_props_files = find_all_props_files(install_dir)
|
||||||
|
|
||||||
|
ninja_inputs = get_ninja_inputs(ninja_binary, ninja_file, goals)
|
||||||
|
logging.debug('')
|
||||||
|
logging.debug('ninja inputs')
|
||||||
|
for ni in ninja_inputs:
|
||||||
|
logging.debug(ni)
|
||||||
|
|
||||||
|
logging.debug('found {} ninja_inputs for goals {}'.format(
|
||||||
|
len(ninja_inputs), goals))
|
||||||
|
|
||||||
|
# Intersect the file_to_info dict with the ninja_inputs to determine
|
||||||
|
# which items from the vendor snapshot are actually used by the goals.
|
||||||
|
|
||||||
|
total_size = 0
|
||||||
|
used_size = 0
|
||||||
|
used_file_to_info = dict()
|
||||||
|
|
||||||
|
for file, size in all_installed_files.items():
|
||||||
|
total_size += size
|
||||||
|
if file in ninja_inputs:
|
||||||
|
logging.debug('used: {}'.format(file))
|
||||||
|
used_size += size
|
||||||
|
info = all_props_files.get(file)
|
||||||
|
|
||||||
|
if info:
|
||||||
|
used_file_to_info[file] = info
|
||||||
|
else:
|
||||||
|
logging.warning('No info for file {}'.format(file))
|
||||||
|
used_file_to_info[file] = 'no info'
|
||||||
|
|
||||||
|
logging.debug('Total size {}'.format(total_size))
|
||||||
|
logging.debug('Used size {}'.format(used_size))
|
||||||
|
logging.debug('')
|
||||||
|
logging.debug('used items')
|
||||||
|
|
||||||
|
used_modules = set()
|
||||||
|
|
||||||
|
for f, i in sorted(used_file_to_info.items()):
|
||||||
|
logging.debug('{} {}'.format(f, i))
|
||||||
|
for m in i:
|
||||||
|
key = 'n=%s,v=%s,a=%s,c=%s,h=%s' % m
|
||||||
|
(name, variation, arch, is_cfi, is_header) = m
|
||||||
|
if not is_header:
|
||||||
|
used_modules.add(key)
|
||||||
|
|
||||||
|
with open(output, 'w') as f:
|
||||||
|
for m in sorted(used_modules):
|
||||||
|
f.write('%s\n' % m)
|
||||||
|
|
||||||
def check_call(cmd):
|
def check_call(cmd):
|
||||||
logging.debug('Running `{}`'.format(' '.join(cmd)))
|
logging.debug('Running `{}`'.format(' '.join(cmd)))
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
@@ -396,6 +588,7 @@ def get_args():
|
|||||||
help='Use symlinks instead of unzipping vendor snapshot zip')
|
help='Use symlinks instead of unzipping vendor snapshot zip')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--install-dir',
|
'--install-dir',
|
||||||
|
required=True,
|
||||||
help=(
|
help=(
|
||||||
'Base directory to which vendor snapshot artifacts are installed. '
|
'Base directory to which vendor snapshot artifacts are installed. '
|
||||||
'Example: --install-dir vendor/<company name>/vendor_snapshot/v30'))
|
'Example: --install-dir vendor/<company name>/vendor_snapshot/v30'))
|
||||||
@@ -404,6 +597,20 @@ def get_args():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help=(
|
help=(
|
||||||
'If provided, does not ask before overwriting the install-dir.'))
|
'If provided, does not ask before overwriting the install-dir.'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--check-module-usage',
|
||||||
|
action='store_true',
|
||||||
|
help='Check which modules are used.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--check-module-usage-goal',
|
||||||
|
action='append',
|
||||||
|
help='Goal(s) for which --check-module-usage is calculated.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--check-module-usage-ninja-file',
|
||||||
|
help='Ninja file for which --check-module-usage is calculated.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--check-module-usage-output',
|
||||||
|
help='File to which to write the check-module-usage results.')
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-v',
|
'-v',
|
||||||
@@ -418,6 +625,34 @@ def main():
|
|||||||
"""Program entry point."""
|
"""Program entry point."""
|
||||||
args = get_args()
|
args = get_args()
|
||||||
|
|
||||||
|
verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
|
||||||
|
verbosity = min(args.verbose, 2)
|
||||||
|
logging.basicConfig(
|
||||||
|
format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
|
||||||
|
level=verbose_map[verbosity])
|
||||||
|
|
||||||
|
if not args.install_dir:
|
||||||
|
raise ValueError('Please provide --install-dir option.')
|
||||||
|
install_dir = os.path.expanduser(args.install_dir)
|
||||||
|
|
||||||
|
if args.check_module_usage:
|
||||||
|
ninja_binary = './prebuilts/build-tools/linux-x86/bin/ninja'
|
||||||
|
|
||||||
|
if not args.check_module_usage_goal:
|
||||||
|
raise ValueError('Please provide --check-module-usage-goal option.')
|
||||||
|
if not args.check_module_usage_ninja_file:
|
||||||
|
raise ValueError(
|
||||||
|
'Please provide --check-module-usage-ninja-file option.')
|
||||||
|
if not args.check_module_usage_output:
|
||||||
|
raise ValueError(
|
||||||
|
'Please provide --check-module-usage-output option.')
|
||||||
|
|
||||||
|
check_module_usage(install_dir, ninja_binary,
|
||||||
|
args.check_module_usage_ninja_file,
|
||||||
|
args.check_module_usage_goal,
|
||||||
|
args.check_module_usage_output)
|
||||||
|
return
|
||||||
|
|
||||||
local = None
|
local = None
|
||||||
if args.local:
|
if args.local:
|
||||||
local = os.path.expanduser(args.local)
|
local = os.path.expanduser(args.local)
|
||||||
@@ -437,18 +672,8 @@ def main():
|
|||||||
'Please provide --branch, --build and --target. Or set --local '
|
'Please provide --branch, --build and --target. Or set --local '
|
||||||
'option.')
|
'option.')
|
||||||
|
|
||||||
if not args.install_dir:
|
|
||||||
raise ValueError('Please provide --install-dir option.')
|
|
||||||
|
|
||||||
snapshot_version = args.snapshot_version
|
snapshot_version = args.snapshot_version
|
||||||
|
|
||||||
verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
|
|
||||||
verbosity = min(args.verbose, 2)
|
|
||||||
logging.basicConfig(
|
|
||||||
format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
|
|
||||||
level=verbose_map[verbosity])
|
|
||||||
|
|
||||||
install_dir = os.path.expanduser(args.install_dir)
|
|
||||||
if os.path.exists(install_dir):
|
if os.path.exists(install_dir):
|
||||||
def remove_dir():
|
def remove_dir():
|
||||||
logging.info('Removing {}'.format(install_dir))
|
logging.info('Removing {}'.format(install_dir))
|
||||||
|
|||||||
Reference in New Issue
Block a user