blob: 019af6a04184b061a93a42a0ea1651664964c16d [file] [log] [blame]
Po-Chien Hsuehf86af512017-01-20 17:02:57 +08001#!/usr/bin/env python3
2
3import argparse
4import fnmatch
5import json
6import os
7import re
8import sys
9
10HELP_MSG = '''
11This script analyses the usage of build-time variables which are defined in BoardConfig*.mk
12and used by framework modules (installed in system.img). Please 'lunch' and 'make' before
13running it.
14'''
15
16TOP = os.environ.get('ANDROID_BUILD_TOP')
17OUT = os.environ.get('OUT')
18
19white_list = [
20 'TARGET_ARCH',
21 'TARGET_ARCH_VARIANT',
22 'TARGET_CPU_VARIANT',
23 'TARGET_CPU_ABI',
24 'TARGET_CPU_ABI2',
25
26 'TARGET_2ND_ARCH',
27 'TARGET_2ND_ARCH_VARIANT',
28 'TARGET_2ND_CPU_VARIANT',
29 'TARGET_2ND_CPU_ABI',
30 'TARGET_2ND_CPU_ABI2',
31
32 'TARGET_NO_BOOTLOADER',
33 'TARGET_NO_KERNEL',
34 'TARGET_NO_RADIOIMAGE',
35 'TARGET_NO_RECOVERY',
36
37 'TARGET_BOARD_PLATFORM',
38
39 'ARCH_ARM_HAVE_ARMV7A',
40 'ARCH_ARM_HAVE_NEON',
41 'ARCH_ARM_HAVE_VFP',
42 'ARCH_ARM_HAVE_VFP_D32',
43
44 'BUILD_NUMBER'
45]
46
47
48# used by find_board_configs_mks() and find_makefiles()
49def find_files(folders, filter):
50 ret = []
51
52 for folder in folders:
53 for root, dirs, files in os.walk(os.path.join(TOP, folder), topdown=True):
54 dirs[:] = [d for d in dirs if not d[0] == '.']
55 for file in files:
56 if filter(file):
57 ret.append(os.path.join(root, file))
58
59 return ret
60
61# find board configs (BoardConfig*.mk)
62def find_board_config_mks(folders = ['build', 'device', 'vendor', 'hardware']):
63 return find_files(folders, lambda x:
64 fnmatch.fnmatch(x, 'BoardConfig*.mk'))
65
66# find makefiles (*.mk or Makefile) under specific folders
67def find_makefiles(folders = ['system', 'frameworks', 'external']):
68 return find_files(folders, lambda x:
69 fnmatch.fnmatch(x, '*.mk') or fnmatch.fnmatch(x, 'Makefile'))
70
71# read module-info.json and find makefiles of modules in system image
72def find_system_module_makefiles():
73 makefiles = []
74 out_system_path = os.path.join(OUT[len(TOP) + 1:], 'system')
75
76 with open(os.path.join(OUT, 'module-info.json')) as module_info_json:
77 module_info = json.load(module_info_json)
78 for module in module_info:
79 installs = module_info[module]['installed']
80 paths = module_info[module]['path']
81
82 installed_in_system = False
83
84 for install in installs:
85 if install.startswith(out_system_path):
86 installed_in_system = True
87 break
88
89 if installed_in_system:
90 for path in paths:
91 makefile = os.path.join(TOP, path, 'Android.mk')
92 makefiles.append(makefile)
93
94 return makefiles
95
96# find variables defined in board_config_mks
97def find_defined_variables(board_config_mks):
98 re_def = re.compile('^[\s]*([\w\d_]*)[\s]*:=')
99 variables = dict()
100
101 for board_config_mk in board_config_mks:
102 for line in open(board_config_mk, encoding='latin1'):
103 mo = re_def.search(line)
104 if mo is None:
105 continue
106
107 variable = mo.group(1)
108 if variable in white_list:
109 continue
110
111 if variable not in variables:
112 variables[variable] = set()
113
114 variables[variable].add(board_config_mk[len(TOP) + 1:])
115
116 return variables
117
118# count variable usage in makefiles
119def find_usage(variable, makefiles):
120 re_usage = re.compile('\$\(' + variable + '\)')
121 usage = set()
122
123 for makefile in makefiles:
124 if not os.path.isfile(makefile):
125 # TODO: support bp
126 continue
127
128 with open(makefile, encoding='latin1') as mk_file:
129 mk_str = mk_file.read()
130
131 if re_usage.search(mk_str) is not None:
132 usage.add(makefile[len(TOP) + 1:])
133
134 return usage
135
136def main():
137 parser = argparse.ArgumentParser(description=HELP_MSG)
138 parser.add_argument("-v", "--verbose",
139 help="print definition and usage locations",
140 action="store_true")
141 args = parser.parse_args()
142
143 print('TOP : ' + TOP)
144 print('OUT : ' + OUT)
145 print()
146
147 sfe_makefiles = find_makefiles()
148 system_module_makefiles = find_system_module_makefiles()
149 board_config_mks = find_board_config_mks()
150 variables = find_defined_variables(board_config_mks)
151
152 if args.verbose:
153 print('sfe_makefiles', len(sfe_makefiles))
154 print('system_module_makefiles', len(system_module_makefiles))
155 print('board_config_mks', len(board_config_mks))
156 print('variables', len(variables))
157 print()
158
159 glossary = (
160 '*Output in CSV format\n\n'
161
162 '*definition count :'
163 ' This variable is defined in how many BoardConfig*.mk\'s\n'
164
165 '*usage in SFE :'
166 ' This variable is used by how many makefiles under system/, frameworks/ and external/ folders\n'
167
168 '*usage in system image :'
169 ' This variable is used by how many system image modules\n')
170
171 csv_string = (
172 'variable name,definition count,usage in SFE,usage in system image\n')
173
174 for variable, locations in sorted(variables.items()):
175 usage_in_sfe = find_usage(variable, sfe_makefiles)
176 usage_of_system_modules = find_usage(variable, system_module_makefiles)
177 usage = usage_in_sfe | usage_of_system_modules
178
179 if len(usage) == 0:
180 continue
181
182 csv_string += ','.join([variable,
183 str(len(locations)),
184 str(len(usage_in_sfe)),
185 str(len(usage_of_system_modules))]) + '\n'
186
187 if args.verbose:
188 print((variable + ' ').ljust(80, '='))
189
190 print('Defined in (' + str(len(locations)) + ') :')
191 for location in sorted(locations):
192 print(' ' + location)
193
194 print('Used in (' + str(len(usage)) + ') :')
195 for location in sorted(usage):
196 print(' ' + location)
197
198 print()
199
200 if args.verbose:
201 print('\n')
202
203 print(glossary)
204 print(csv_string)
205
206if __name__ == '__main__':
207 if TOP is None:
208 sys.exit('$ANDROID_BUILD_TOP is undefined, please lunch and make before running this script')
209
210 if OUT is None:
211 sys.exit('$OUT is undefined, please lunch and make before running this script')
212
213 if not os.path.isfile(os.path.join(OUT, 'module-info.json')):
214 sys.exit('module-info.json is missing, please lunch and make before running this script')
215
216 main()
217