1#!/usr/bin/env python 2 3import os 4import subprocess 5import sys 6from antlr4 import * 7from gnparser.gnLexer import gnLexer 8from gnparser.gnParser import gnParser 9from gnparser.gnListener import gnListener 10from string import Template 11 12DBG = False 13 14# Reformat the specified Android.bp file 15def _bpFmt(filename): 16 ## NOTE: bpfmt does not set error code even when the bp file is illegal. 17 print subprocess.check_output(["bpfmt", "-w", filename]) 18 19def _bpList(entries): 20 return '[' + ",".join(['"' + x + '"' for x in entries]) + ']' 21 22# Write an Android.bp in the simpler format used by v8_libplatform and 23# v8_libsampler 24def _writeBP(filename, module_name, sources): 25 if not sources: 26 raise ValueError('No sources for ' + filename) 27 28 with open(filename, 'w') as out: 29 out.write(Template(''' 30 // GENERATED, do not edit 31 // for changes, see genmakefiles.py 32 cc_library_static { 33 name: "$module_name", 34 defaults: ["v8_defaults"], 35 srcs: $srcs, 36 local_include_dirs: ["src", "include"], 37 } 38 ''').substitute({'module_name': module_name, 'srcs' : _bpList(sorted(sources))})) 39 40 _bpFmt(filename) 41 42 43def _writeV8SrcBP(getSourcesFunc): 44 sources = getSourcesFunc(None) 45 if not sources: 46 raise ValueError('Must specify v8_base target properties') 47 sources.add('src/setup-isolate-full.cc') 48 # sources.add('src/builtins/setup-builtins-internal.cc') 49 # sources.add('src/interpreter/setup-interpreter-internal.cc') 50 arm_src = list(getSourcesFunc('arm') - sources) 51 arm64_src = list(getSourcesFunc('arm64') - sources) 52 x86_src = list(getSourcesFunc('x86') - sources) 53 x86_64_src = list(getSourcesFunc('x64') - sources) 54 mips_src = list(getSourcesFunc('mips') - sources) 55 mips64_src = list(getSourcesFunc('mips64') - sources) 56 57 filename = 'Android.v8.bp' 58 with open(filename, 'w') as out: 59 out.write(Template(''' 60 // GENERATED, do not edit 61 // for changes, see genmakefiles.py 62 cc_library_static { 63 name: "libv8src", 64 defaults: ["v8_defaults"], 65 srcs: $srcs, 66 arch: { 67 arm: { 68 srcs: $arm_src, 69 }, 70 arm64: { 71 srcs: $arm64_src, 72 }, 73 mips: { 74 srcs: $mips_src, 75 }, 76 mips64: { 77 srcs: $mips64_src, 78 }, 79 x86: { 80 srcs: $x86_src, 81 }, 82 x86_64: { 83 srcs: $x86_64_src, 84 }, 85 }, 86 target: { 87 android: { 88 cflags: ["-DANDROID_LINK_SHARED_ICU4C"], 89 }, 90 }, 91 local_include_dirs: ["src"], 92 header_libs: ["libicuuc_headers", "libicui18n_headers"], 93 generated_headers: ["v8_torque_file"], 94 generated_sources: ["v8_torque_file_cc"], 95 } 96 ''').substitute({'srcs': _bpList(sorted(sources)), 97 'arm_src': _bpList(sorted(arm_src)), 98 'arm64_src': _bpList(sorted(arm64_src)), 99 'mips_src': _bpList(sorted(mips_src)), 100 'mips64_src': _bpList(sorted(mips64_src)), 101 'x86_src': _bpList(sorted(x86_src)), 102 'x86_64_src': _bpList(sorted(x86_64_src)), 103 })) 104 105 _bpFmt(filename) 106 107def _writeGeneratedFilesBP(sources): 108 if not sources: 109 raise ValueError('Must specify j2sc target properties') 110 111 filename = 'Android.v8gen.bp' 112 with open(filename, 'w') as out: 113 out.write(Template(''' 114 // GENERATED, do not edit 115 // for changes, see genmakefiles.py 116 filegroup { 117 name: "v8_js_lib_files", 118 srcs: $srcs, 119 } 120 ''').substitute({'srcs' : _bpList(sources)})) ## Not sorted intentionally 121 122 _bpFmt(filename) 123 124def _writeLibBaseBP(sources): 125 if not sources: 126 raise ValueError('Must specify v8_libbase target properties') 127 128 filename = 'Android.base.bp' 129 with open(filename, 'w') as out: 130 out.write(Template(''' 131 // GENERATED, do not edit 132 // for changes, see genmakefiles.py 133 cc_library_static { 134 name: "libv8base", 135 defaults: ["v8_defaults"], 136 host_supported: true, 137 srcs: $srcs, 138 local_include_dirs: ["src"], 139 target: { 140 android: { 141 srcs: ["src/base/debug/stack_trace_android.cc"], 142 }, 143 linux: { 144 srcs: ["src/base/platform/platform-linux.cc"], 145 }, 146 host: { 147 srcs: ["src/base/debug/stack_trace_posix.cc"], 148 cflags: ["-UANDROID"], 149 }, 150 darwin: { 151 srcs: ["src/base/platform/platform-macos.cc"], 152 }, 153 }, 154 } 155 ''').substitute({'srcs' : _bpList(sorted(sources))})) 156 157 _bpFmt(filename) 158 159 160def _expr_to_str(expr): 161 val = expr.unaryexpr().primaryexpr() 162 if val.String(): 163 return val.String().getText()[1:-1] ## Strip quotation marks around string 164 elif val.Identifier(): 165 return val.Identifier().getText() 166 else: 167 if DBG: print 'WARN: unhandled primary expression' 168 return None 169 170class V8GnListener(gnListener): 171 def __init__(self, target, arch, only_cc_files): 172 super(gnListener, self).__init__() 173 self._match = False 174 self._depth = 0 175 self._target = target 176 self._arch = arch 177 self._sources = [] 178 self._fixed_conditions = { 179 'use_jumbo_build' : True, 180 'use_jumbo_build==true' : True, 181 'is_win' : False, 182 'is_linux' : False, 183 'v8_postmortem_support' : False, 184 'v8_enable_i18n_support': True, 185 '!v8_enable_i18n_support': False, 186 'current_os!="aix"' : True, 187 'is_posix||is_fuchsia' : True, 188 'v8_current_cpu=="arm"' : arch == 'arm', 189 'v8_current_cpu=="arm64"' : arch == 'arm64', 190 'v8_current_cpu=="x86"' : arch == 'x86', 191 'v8_current_cpu=="x64"' : arch == 'x64', 192 'v8_current_cpu=="mips"||v8_current_cpu=="mipsel"' : arch == 'mips', 193 'v8_current_cpu=="mips64"||v8_current_cpu=="mips64el"' : arch == 'mips64', 194 'v8_current_cpu=="ppc"||v8_current_cpu=="ppc64"' : False, 195 'v8_current_cpu=="s390"||v8_current_cpu=="s390x"' : False, 196 197 } 198 self._only_cc_files = only_cc_files 199 200 def _match_call_target(self, ctx): 201 call_type = ctx.Identifier().getText() 202 if not call_type in ['v8_source_set', 'v8_component', 'action']: return False 203 call_name = _expr_to_str(ctx.exprlist().expr(0)) 204 return call_name == self._target 205 206 def enterCall(self, ctx): 207 if self._depth == 1 and self._match_call_target(ctx): 208 self._match = True 209 self._conditions = [] ## [(value, condition), ...] 210 if DBG: print 'Found call', str(ctx.Identifier()), ctx.exprlist().getText() 211 212 def exitCall(self, ctx): 213 if self._match and self._match_call_target(ctx): 214 self._match = False 215 self._conditions = [] 216 if DBG: print 'Left call' 217 218 def _extract_sources(self, ctx): 219 op = ctx.AssignOp().getText() 220 if not ctx.expr().unaryexpr().primaryexpr().exprlist(): 221 ## sources += check_header_includes_sources 222 return 223 srcs = map(_expr_to_str, ctx.expr().unaryexpr().primaryexpr().exprlist().expr()) 224 if self._only_cc_files: 225 srcs = [x for x in srcs if x.endswith('.cc')] 226 if DBG: print '_extract_sources: ', len(srcs), "condition:", self._conditions 227 if op == '=': 228 if self._sources: 229 print "WARN: override sources" 230 self._sources = srcs 231 elif op == '+=': 232 self._sources.extend(srcs) 233 234 def _compute_condition(self, ctx): 235 condition = ctx.expr().getText() 236 if DBG: print '_extract_condition', condition 237 if condition in self._fixed_conditions: 238 result = self._fixed_conditions[condition] 239 else: 240 print 'WARN: unknown condition, assume False', condition 241 self._fixed_conditions[condition] = False 242 result = False 243 if DBG: print 'Add condition:', condition 244 self._conditions.append((result, condition)) 245 246 247 def enterCondition(self, ctx): 248 if not self._match: return 249 self._compute_condition(ctx) 250 251 def enterElsec(self, ctx): 252 if not self._match: return 253 c = self._conditions[-1] 254 self._conditions[-1] = (not c[0], c[1]) 255 if DBG: print 'Negate condition:', self._conditions[-1] 256 257 def exitCondition(self, ctx): 258 if not self._match: return 259 if DBG: print 'Remove conditions: ', self._conditions[-1] 260 del self._conditions[-1] 261 262 def _flatten_conditions(self): 263 if DBG: print '_flatten_conditions: ', self._conditions 264 for condition, _ in self._conditions: 265 if not condition: 266 return False 267 return True 268 269 def enterAssignment(self, ctx): 270 if not self._match: return 271 if ctx.lvalue().Identifier().getText() == "sources": 272 if self._flatten_conditions(): 273 self._extract_sources(ctx) 274 275 def enterStatement(self, ctx): 276 self._depth += 1 277 278 def exitStatement(self, ctx): 279 self._depth -= 1 280 281 def get_sources(self): 282 seen = set() 283 result = [] 284 ## Deduplicate list while maintaining ordering. needed for js2c files 285 for s in self._sources: 286 if not s in seen: 287 result.append(s) 288 seen.add(s) 289 return result 290 291def parseSources(tree, target, arch = None, only_cc_files = True): 292 listener = V8GnListener(target, arch, only_cc_files) 293 ParseTreeWalker().walk(listener, tree) 294 return listener.get_sources() 295 296def GenerateMakefiles(): 297 f = FileStream(os.path.join(os.getcwd(), './BUILD.gn')) 298 lexer = gnLexer(f) 299 stream = CommonTokenStream(lexer) 300 parser = gnParser(stream) 301 tree = parser.r() 302 303 _writeBP('Android.platform.bp', 'libv8platform', parseSources(tree, "v8_libplatform")) 304 _writeBP('Android.sampler.bp', 'libv8sampler', parseSources(tree, "v8_libsampler")) 305 _writeV8SrcBP(lambda arch: set(parseSources(tree, "v8_base", arch) + parseSources(tree, "v8_initializers", arch))) 306 _writeGeneratedFilesBP(parseSources(tree, "js2c", None, False)) 307 _writeLibBaseBP(parseSources(tree, "v8_libbase")) 308 309if __name__ == '__main__': 310 GenerateMakefiles() 311