You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
5.9 KiB
217 lines
5.9 KiB
#!/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() |
|
|
|
|