#!/usr/bin/env python import os import subprocess import sys from antlr4 import * from gnparser.gnLexer import gnLexer from gnparser.gnParser import gnParser from gnparser.gnListener import gnListener from string import Template DBG = False # Reformat the specified Android.bp file def _bpFmt(filename): ## NOTE: bpfmt does not set error code even when the bp file is illegal. print subprocess.check_output(["bpfmt", "-w", filename]) def _bpList(entries): return '[' + ",".join(['"' + x + '"' for x in entries]) + ']' # Write an Android.bp in the simpler format used by v8_libplatform and # v8_libsampler def _writeBP(filename, module_name, sources): if not sources: raise ValueError('No sources for ' + filename) with open(filename, 'w') as out: out.write(Template(''' // GENERATED, do not edit // for changes, see genmakefiles.py cc_library_static { name: "$module_name", defaults: ["v8_defaults"], srcs: $srcs, local_include_dirs: ["src", "include"], apex_available: [ "com.android.i18n", ], } ''').substitute({'module_name': module_name, 'srcs' : _bpList(sorted(sources))})) _bpFmt(filename) def _writeFileGroupBP(filename, module_name, sources): if not sources: raise ValueError('No sources for ' + filename) with open(filename, 'w') as out: out.write(Template(''' // GENERATED, do not edit // for changes, see genmakefiles.py filegroup { name: "$module_name", srcs: $srcs, } ''').substitute({'module_name': module_name, 'srcs' : _bpList(sorted(sources))})) _bpFmt(filename) def _writeV8BaseBP(getSourcesFunc): sources = getSourcesFunc(None) if not sources: raise ValueError('Must specify v8_base target properties') sources.add('src/heap/base/stack.cc') sources.add('src/heap/base/worklist.cc') sources.add('third_party/zlib/google/compression_utils_portable.cc') arm_src = list(getSourcesFunc('arm') - sources) arm64_src = list(getSourcesFunc('arm64') - sources) x86_src = list(getSourcesFunc('x86') - sources) x86_64_src = list(getSourcesFunc('x64') - sources) filename = 'Android.base.bp' with open(filename, 'w') as out: out.write(Template(''' // GENERATED, do not edit // for changes, see genmakefiles.py cc_library_static { name: "v8_base", defaults: ["v8_defaults", "v8_torque_headers"], srcs: $srcs, arch: { arm: { srcs: $arm_src, generated_sources: [ "v8_torque_file_cc_32", ], }, arm64: { srcs: $arm64_src, generated_sources: [ "v8_torque_file_cc", ], }, x86: { srcs: $x86_src, generated_sources: [ "v8_torque_file_cc_32", ], }, x86_64: { srcs: $x86_64_src, generated_sources: [ "v8_torque_file_cc", ], }, }, local_include_dirs: ["src", "include", "third_party/zlib",], generated_headers: ["v8_generate_bytecode_builtins_list"], sanitize: { cfi: true, blocklist: "./tools/cfi/blacklist.txt", }, apex_available: [ "com.android.i18n", ], } ''').substitute({'srcs': _bpList(sorted(sources)), 'arm_src': _bpList(sorted(arm_src)), 'arm64_src': _bpList(sorted(arm64_src)), 'x86_src': _bpList(sorted(x86_src)), 'x86_64_src': _bpList(sorted(x86_64_src)), })) _bpFmt(filename) def _writeLibBaseBP(sources): if not sources: raise ValueError('Must specify v8_libbase target properties') filename = 'Android.libbase.bp' with open(filename, 'w') as out: out.write(Template(''' // GENERATED, do not edit // for changes, see genmakefiles.py cc_library_static { name: "v8_libbase", defaults: ["v8_defaults"], host_supported: true, srcs: $srcs, local_include_dirs: ["src"], apex_available: [ "com.android.i18n", ], target: { android: { srcs: [ "src/base/debug/stack_trace_android.cc", "src/base/platform/platform-linux.cc", ], }, host: { srcs: [ "src/base/debug/stack_trace_posix.cc", "src/base/platform/platform-linux.cc", ], cflags: ["-UANDROID"], }, }, } ''').substitute({'srcs' : _bpList(sorted(sources))})) _bpFmt(filename) def _writeTorqueBP(sources): if not sources: raise ValueError('Must specify torque_files target properties') genfiles = [ "torque-generated/bit-fields.h", "torque-generated/builtin-definitions.h", "torque-generated/interface-descriptors.inc", "torque-generated/factory.cc", "torque-generated/factory.inc", "torque-generated/field-offsets.h", "torque-generated/class-verifiers.cc", "torque-generated/class-verifiers.h", "torque-generated/enum-verifiers.cc", "torque-generated/objects-printer.cc", "torque-generated/objects-body-descriptors-inl.inc", # "torque-generated/class-debug-readers.cc", # "torque-generated/class-debug-readers.h", "torque-generated/exported-macros-assembler.cc", "torque-generated/exported-macros-assembler.h", "torque-generated/csa-types.h", "torque-generated/instance-types.h", "torque-generated/runtime-macros.cc", "torque-generated/runtime-macros.h", "torque-generated/class-forward-declarations.h", ] intl_files = set([ "src/objects/intl-objects.tq", "src/objects/js-break-iterator.tq", "src/objects/js-collator.tq", "src/objects/js-date-time-format.tq", "src/objects/js-display-names.tq", "src/objects/js-list-format.tq", "src/objects/js-locale.tq", "src/objects/js-number-format.tq", "src/objects/js-plural-rules.tq", "src/objects/js-relative-time-format.tq", "src/objects/js-segment-iterator.tq", "src/objects/js-segmenter.tq", "src/objects/js-segments.tq" ]) sources = [s for s in sources if not s in intl_files] #sources = sources[:1] #sources = filter(lambda s:not s.startswith("test/"), sources) for tq in sources: filetq = tq.replace('.tq', '-tq') genfiles.append("torque-generated/" + filetq + "-csa.cc") genfiles.append("torque-generated/" + filetq + "-csa.h") genfiles.append("torque-generated/" + filetq + "-inl.inc") genfiles.append("torque-generated/" + filetq + ".cc") genfiles.append("torque-generated/" + filetq + ".inc") dirs = set(map(os.path.dirname, sources)) cc_files = filter(lambda s:s.endswith(".cc"), genfiles) include_files = filter(lambda s:not s.endswith(".cc"), genfiles) filename = 'Android.torque.bp' with open(filename, 'w') as out: out.write(Template(''' // GENERATED, do not edit // for changes, see genmakefiles.py filegroup { name: "v8_torque_src_files", srcs: $srcs, } genrule { name: "v8_torque_file", tools: ["v8_torque"], srcs: [":v8_torque_src_files"], cmd: $cmd, out: $include_files, } genrule { name: "v8_torque_file_cc", tools: ["v8_torque"], srcs: [":v8_torque_src_files"], cmd: $cmd, out: $cc_files, } genrule { name: "v8_torque_file_32", tools: ["v8_torque"], srcs: [":v8_torque_src_files"], cmd: $cmd32, out: $include_files, } genrule { name: "v8_torque_file_cc_32", tools: ["v8_torque"], srcs: [":v8_torque_src_files"], cmd: $cmd32, out: $cc_files, } ''').substitute({ 'srcs' : _bpList(sources), 'cmd' : '''"mkdir -p $(genDir)/torque-generated/ && pushd . && cd $(genDir)/torque-generated/ && mkdir -p ''' + ' '.join(dirs) + ''' && popd && $(location v8_torque) -o $(genDir)/torque-generated/ -v8-root $$(pwd)/external/v8 $$(echo $(in) | sed 's/external.v8.//g'); true"''', 'cmd32' : '''"mkdir -p $(genDir)/torque-generated/ && pushd . && cd $(genDir)/torque-generated/ && mkdir -p ''' + ' '.join(dirs) + ''' && popd && $(location v8_torque) -o $(genDir)/torque-generated/ -v8-root $$(pwd)/external/v8 -m32 $$(echo $(in) | sed 's/external.v8.//g'); true"''', 'cc_files' : _bpList(cc_files), 'include_files' : _bpList(include_files)})) _bpFmt(filename) def _expr_to_str(expr): val = expr.unaryexpr().primaryexpr() if val.String(): return val.String().getText()[1:-1] ## Strip quotation marks around string elif val.Identifier(): return val.Identifier().getText() else: if DBG: print 'WARN: unhandled primary expression' return None class V8GnListener(gnListener): def __init__(self, target, arch, only_cc_files): super(gnListener, self).__init__() self._match = False self._depth = 0 self._target = target self._arch = arch self._sources = [] self._fixed_conditions = { 'use_jumbo_build' : True, 'use_jumbo_build==true' : True, 'is_win' : False, 'is_linux' : False, 'v8_postmortem_support' : False, 'v8_enable_i18n_support': False, '!v8_enable_i18n_support': True, 'current_os!="aix"' : True, 'is_posix||is_fuchsia' : True, 'v8_current_cpu=="arm"' : arch == 'arm', 'v8_current_cpu=="arm64"' : arch == 'arm64', 'v8_current_cpu=="x86"' : arch == 'x86', 'v8_current_cpu=="x64"' : arch == 'x64', 'v8_current_cpu=="mips"||v8_current_cpu=="mipsel"' : arch == 'mips', 'v8_current_cpu=="mips64"||v8_current_cpu=="mips64el"' : arch == 'mips64', 'v8_current_cpu=="ppc"||v8_current_cpu=="ppc64"' : False, 'v8_current_cpu=="s390"||v8_current_cpu=="s390x"' : False, 'is_linux||is_chromeos||is_mac||is_ios||target_os=="freebsd"' : True, } self._only_cc_files = only_cc_files def _match_call_target(self, ctx): call_type = ctx.Identifier().getText() if not call_type in ['v8_source_set', 'v8_component', 'action']: return False call_name = _expr_to_str(ctx.exprlist().expr(0)) return call_name == self._target def enterCall(self, ctx): if self._depth == 1 and self._match_call_target(ctx): self._match = True self._conditions = [] ## [(value, condition), ...] if DBG: print 'Found call', str(ctx.Identifier()), ctx.exprlist().getText() def exitCall(self, ctx): if self._match and self._match_call_target(ctx): self._match = False self._conditions = [] if DBG: print 'Left call' def _extract_sources(self, ctx): op = ctx.AssignOp().getText() if not ctx.expr().unaryexpr().primaryexpr().exprlist(): ## sources += check_header_includes_sources return srcs = map(_expr_to_str, ctx.expr().unaryexpr().primaryexpr().exprlist().expr()) if self._only_cc_files: srcs = [x for x in srcs if x.endswith('.cc')] if DBG: print '_extract_sources: ', len(srcs), "condition:", self._conditions if op == '=': if self._sources: print "WARN: override sources" self._sources = srcs elif op == '+=': self._sources.extend(srcs) def _compute_condition(self, ctx): condition = ctx.expr().getText() if DBG: print '_extract_condition', condition if condition in self._fixed_conditions: result = self._fixed_conditions[condition] else: print 'WARN: unknown condition, assume False', condition self._fixed_conditions[condition] = False result = False if DBG: print 'Add condition:', condition self._conditions.append((result, condition)) def enterCondition(self, ctx): if not self._match: return self._compute_condition(ctx) def enterElsec(self, ctx): if not self._match: return c = self._conditions[-1] self._conditions[-1] = (not c[0], c[1]) if DBG: print 'Negate condition:', self._conditions[-1] def exitCondition(self, ctx): if not self._match: return if DBG: print 'Remove conditions: ', self._conditions[-1] del self._conditions[-1] def _flatten_conditions(self): if DBG: print '_flatten_conditions: ', self._conditions for condition, _ in self._conditions: if not condition: return False return True def enterAssignment(self, ctx): if not self._match: return if ctx.lvalue().Identifier().getText() == "sources": if self._flatten_conditions(): self._extract_sources(ctx) def enterStatement(self, ctx): self._depth += 1 def exitStatement(self, ctx): self._depth -= 1 def get_sources(self): seen = set() result = [] ## Deduplicate list while maintaining ordering. needed for js2c files for s in self._sources: if not s in seen: result.append(s) seen.add(s) return result def parseSources(tree, target, arch = None, only_cc_files = True): listener = V8GnListener(target, arch, only_cc_files) ParseTreeWalker().walk(listener, tree) return listener.get_sources() class V8VariableListener(gnListener): def __init__(self, variableName): super(gnListener, self).__init__() self._sources = [] self._variable = variableName def enterAssignment(self, ctx): if ctx.lvalue().Identifier().getText() == self._variable: op = ctx.AssignOp().getText() srcs = map(_expr_to_str, ctx.expr().unaryexpr().primaryexpr().exprlist().expr()) if DBG: print self._variable, ':', len(srcs) if op == '=': if self._sources: print "WARN: override", self._variable self._sources = srcs elif op == '+=': self._sources.extend(srcs) def get_sources(self): return self._sources def parseTorqueSources(tree): listener = V8VariableListener("torque_files") ParseTreeWalker().walk(listener, tree) return listener.get_sources() def getV8BaseSourceFunc(tree): intl_files = set([ "src/builtins/builtins-intl.cc", "src/objects/intl-objects.cc", "src/objects/intl-objects.h", "src/objects/js-break-iterator-inl.h", "src/objects/js-break-iterator.cc", "src/objects/js-break-iterator.h", "src/objects/js-collator-inl.h", "src/objects/js-collator.cc", "src/objects/js-collator.h", "src/objects/js-date-time-format-inl.h", "src/objects/js-date-time-format.cc", "src/objects/js-date-time-format.h", "src/objects/js-display-names-inl.h", "src/objects/js-display-names.cc", "src/objects/js-display-names.h", "src/objects/js-list-format-inl.h", "src/objects/js-list-format.cc", "src/objects/js-list-format.h", "src/objects/js-locale-inl.h", "src/objects/js-locale.cc", "src/objects/js-locale.h", "src/objects/js-number-format-inl.h", "src/objects/js-number-format.cc", "src/objects/js-number-format.h", "src/objects/js-plural-rules-inl.h", "src/objects/js-plural-rules.cc", "src/objects/js-plural-rules.h", "src/objects/js-relative-time-format-inl.h", "src/objects/js-relative-time-format.cc", "src/objects/js-relative-time-format.h", "src/objects/js-segment-iterator-inl.h", "src/objects/js-segment-iterator.cc", "src/objects/js-segment-iterator.h", "src/objects/js-segmenter-inl.h", "src/objects/js-segmenter.cc", "src/objects/js-segmenter.h", "src/objects/js-segments-inl.h", "src/objects/js-segments.cc", "src/objects/js-segments.h", "src/runtime/runtime-intl.cc", "src/strings/char-predicates.cc", "src/builtins/builtins-intl-gen.cc", ]) listener = V8VariableListener("v8_compiler_sources") ParseTreeWalker().walk(listener, tree) v8_compiler_sources = listener.get_sources() v8_compiler_sources = [x for x in v8_compiler_sources if x.endswith('.cc')] def getSourceFunc(arch): return set(parseSources(tree, "v8_base_without_compiler", arch) + v8_compiler_sources) \ - intl_files return getSourceFunc def GenerateMakefiles(): f = FileStream(os.path.join(os.getcwd(), './BUILD.gn')) lexer = gnLexer(f) stream = CommonTokenStream(lexer) parser = gnParser(stream) tree = parser.r() _writeLibBaseBP(parseSources(tree, "v8_libbase")) _writeBP('Android.libplatform.bp', 'v8_libplatform', parseSources(tree, "v8_libplatform")) _writeBP('Android.libsampler.bp', 'v8_libsampler', parseSources(tree, "v8_libsampler")) _writeV8BaseBP(getV8BaseSourceFunc(tree)) v8_init_sources = parseSources(tree, "v8_initializers") v8_init_sources.remove("src/builtins/builtins-intl-gen.cc") _writeFileGroupBP('Android.initializers.bp', 'v8_initializers', v8_init_sources) _writeTorqueBP(parseTorqueSources(tree)) if __name__ == '__main__': GenerateMakefiles()