1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
#!/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()
|