| #!/usr/bin/env python3 |
| |
| import argparse |
| import fnmatch |
| import json |
| import os |
| import re |
| import sys |
| |
| HELP_MSG = ''' |
| This script analyses the usage of build-time variables which are defined in BoardConfig*.mk |
| and used by framework modules (installed in system.img). Please 'lunch' and 'make' before |
| running it. |
| ''' |
| |
| TOP = os.environ.get('ANDROID_BUILD_TOP') |
| OUT = os.environ.get('OUT') |
| |
| white_list = [ |
| 'TARGET_ARCH', |
| 'TARGET_ARCH_VARIANT', |
| 'TARGET_CPU_VARIANT', |
| 'TARGET_CPU_ABI', |
| 'TARGET_CPU_ABI2', |
| |
| 'TARGET_2ND_ARCH', |
| 'TARGET_2ND_ARCH_VARIANT', |
| 'TARGET_2ND_CPU_VARIANT', |
| 'TARGET_2ND_CPU_ABI', |
| 'TARGET_2ND_CPU_ABI2', |
| |
| 'TARGET_NO_BOOTLOADER', |
| 'TARGET_NO_KERNEL', |
| 'TARGET_NO_RADIOIMAGE', |
| 'TARGET_NO_RECOVERY', |
| |
| 'TARGET_BOARD_PLATFORM', |
| |
| 'ARCH_ARM_HAVE_ARMV7A', |
| 'ARCH_ARM_HAVE_NEON', |
| 'ARCH_ARM_HAVE_VFP', |
| 'ARCH_ARM_HAVE_VFP_D32', |
| |
| 'BUILD_NUMBER' |
| ] |
| |
| |
| # used by find_board_configs_mks() and find_makefiles() |
| def find_files(folders, filter): |
| ret = [] |
| |
| for folder in folders: |
| for root, dirs, files in os.walk(os.path.join(TOP, folder), topdown=True): |
| dirs[:] = [d for d in dirs if not d[0] == '.'] |
| for file in files: |
| if filter(file): |
| ret.append(os.path.join(root, file)) |
| |
| return ret |
| |
| # find board configs (BoardConfig*.mk) |
| def find_board_config_mks(folders = ['build', 'device', 'vendor', 'hardware']): |
| return find_files(folders, lambda x: |
| fnmatch.fnmatch(x, 'BoardConfig*.mk')) |
| |
| # find makefiles (*.mk or Makefile) under specific folders |
| def find_makefiles(folders = ['system', 'frameworks', 'external']): |
| return find_files(folders, lambda x: |
| fnmatch.fnmatch(x, '*.mk') or fnmatch.fnmatch(x, 'Makefile')) |
| |
| # read module-info.json and find makefiles of modules in system image |
| def find_system_module_makefiles(): |
| makefiles = [] |
| out_system_path = os.path.join(OUT[len(TOP) + 1:], 'system') |
| |
| with open(os.path.join(OUT, 'module-info.json')) as module_info_json: |
| module_info = json.load(module_info_json) |
| for module in module_info: |
| installs = module_info[module]['installed'] |
| paths = module_info[module]['path'] |
| |
| installed_in_system = False |
| |
| for install in installs: |
| if install.startswith(out_system_path): |
| installed_in_system = True |
| break |
| |
| if installed_in_system: |
| for path in paths: |
| makefile = os.path.join(TOP, path, 'Android.mk') |
| makefiles.append(makefile) |
| |
| return makefiles |
| |
| # find variables defined in board_config_mks |
| def find_defined_variables(board_config_mks): |
| re_def = re.compile('^[\s]*([\w\d_]*)[\s]*:=') |
| variables = dict() |
| |
| for board_config_mk in board_config_mks: |
| for line in open(board_config_mk, encoding='latin1'): |
| mo = re_def.search(line) |
| if mo is None: |
| continue |
| |
| variable = mo.group(1) |
| if variable in white_list: |
| continue |
| |
| if variable not in variables: |
| variables[variable] = set() |
| |
| variables[variable].add(board_config_mk[len(TOP) + 1:]) |
| |
| return variables |
| |
| # count variable usage in makefiles |
| def find_usage(variable, makefiles): |
| re_usage = re.compile('\$\(' + variable + '\)') |
| usage = set() |
| |
| for makefile in makefiles: |
| if not os.path.isfile(makefile): |
| # TODO: support bp |
| continue |
| |
| with open(makefile, encoding='latin1') as mk_file: |
| mk_str = mk_file.read() |
| |
| if re_usage.search(mk_str) is not None: |
| usage.add(makefile[len(TOP) + 1:]) |
| |
| return usage |
| |
| def main(): |
| parser = argparse.ArgumentParser(description=HELP_MSG) |
| parser.add_argument("-v", "--verbose", |
| help="print definition and usage locations", |
| action="store_true") |
| args = parser.parse_args() |
| |
| print('TOP : ' + TOP) |
| print('OUT : ' + OUT) |
| print() |
| |
| sfe_makefiles = find_makefiles() |
| system_module_makefiles = find_system_module_makefiles() |
| board_config_mks = find_board_config_mks() |
| variables = find_defined_variables(board_config_mks) |
| |
| if args.verbose: |
| print('sfe_makefiles', len(sfe_makefiles)) |
| print('system_module_makefiles', len(system_module_makefiles)) |
| print('board_config_mks', len(board_config_mks)) |
| print('variables', len(variables)) |
| print() |
| |
| glossary = ( |
| '*Output in CSV format\n\n' |
| |
| '*definition count :' |
| ' This variable is defined in how many BoardConfig*.mk\'s\n' |
| |
| '*usage in SFE :' |
| ' This variable is used by how many makefiles under system/, frameworks/ and external/ folders\n' |
| |
| '*usage in system image :' |
| ' This variable is used by how many system image modules\n') |
| |
| csv_string = ( |
| 'variable name,definition count,usage in SFE,usage in system image\n') |
| |
| for variable, locations in sorted(variables.items()): |
| usage_in_sfe = find_usage(variable, sfe_makefiles) |
| usage_of_system_modules = find_usage(variable, system_module_makefiles) |
| usage = usage_in_sfe | usage_of_system_modules |
| |
| if len(usage) == 0: |
| continue |
| |
| csv_string += ','.join([variable, |
| str(len(locations)), |
| str(len(usage_in_sfe)), |
| str(len(usage_of_system_modules))]) + '\n' |
| |
| if args.verbose: |
| print((variable + ' ').ljust(80, '=')) |
| |
| print('Defined in (' + str(len(locations)) + ') :') |
| for location in sorted(locations): |
| print(' ' + location) |
| |
| print('Used in (' + str(len(usage)) + ') :') |
| for location in sorted(usage): |
| print(' ' + location) |
| |
| print() |
| |
| if args.verbose: |
| print('\n') |
| |
| print(glossary) |
| print(csv_string) |
| |
| if __name__ == '__main__': |
| if TOP is None: |
| sys.exit('$ANDROID_BUILD_TOP is undefined, please lunch and make before running this script') |
| |
| if OUT is None: |
| sys.exit('$OUT is undefined, please lunch and make before running this script') |
| |
| if not os.path.isfile(os.path.join(OUT, 'module-info.json')): |
| sys.exit('module-info.json is missing, please lunch and make before running this script') |
| |
| main() |
| |