1#!/usr/bin/env python3 2# Copyright (C) 2020 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17import os 18import subprocess 19import shutil 20import sys 21import tempfile 22 23GRAMMAR_FOOTER = ''' 24%token SPACE ILLEGAL. 25''' 26 27KEYWORDHASH_HEADER = ''' 28#include "src/trace_processor/perfetto_sql/grammar/perfettosql_keywordhash_helper.h" 29''' 30 31KEYWORD_END = ''' 32 { "WITHOUT", "TK_WITHOUT", ALWAYS, 1 }, 33};''' 34 35KEYWORD_END_REPLACE = ''' 36 { "WITHOUT", "TK_WITHOUT", ALWAYS, 1 }, 37 { "PERFETTO", "TK_PERFETTO", ALWAYS, 1 }, 38 { "MACRO", "TK_MACRO", ALWAYS, 1 }, 39 { "INCLUDE", "TK_INCLUDE", ALWAYS, 1 }, 40 { "MODULE", "TK_MODULE", ALWAYS, 1 }, 41 { "RETURNS", "TK_RETURNS", ALWAYS, 1 }, 42 { "FUNCTION", "TK_FUNCTION", ALWAYS, 1 }, 43};''' 44 45 46def copy_tokenizer(args: argparse.Namespace): 47 shutil.copy(args.sqlite_tokenize, args.sqlite_tokenize_out) 48 49 with open(args.sqlite_tokenize_out, 'r+', encoding='utf-8') as fp: 50 res: str = fp.read() 51 idx = res.find('/*\n** Run the parser on the given SQL string.') 52 assert idx != -1 53 res = res[0:idx] 54 res = res.replace( 55 '#include "sqliteInt.h"', 56 '#include "src/trace_processor/perfetto_sql/tokenizer/tokenize_internal_helper.h"', 57 ) 58 res = res.replace('#include "keywordhash.h"\n', '') 59 fp.seek(0) 60 fp.write(res) 61 fp.truncate() 62 63 64def main(): 65 parser = argparse.ArgumentParser() 66 parser.add_argument( 67 '--lemon', default=os.path.normpath('buildtools/sqlite_src/tool/lemon.c')) 68 parser.add_argument( 69 '--mkkeywordhash', 70 default=os.path.normpath('buildtools/sqlite_src/tool/mkkeywordhash.c')) 71 parser.add_argument( 72 '--lemon-template', 73 default=os.path.normpath('buildtools/sqlite_src/tool/lempar.c')) 74 parser.add_argument( 75 '--clang', default=os.path.normpath('buildtools/linux64/clang/bin/clang')) 76 parser.add_argument( 77 '--preprocessor-grammar', 78 default=os.path.normpath( 79 'src/trace_processor/perfetto_sql/preprocessor/preprocessor_grammar.y' 80 ), 81 ) 82 parser.add_argument( 83 '--sqlite-grammar', 84 default=os.path.normpath('buildtools/sqlite_src/src/parse.y'), 85 ) 86 parser.add_argument( 87 '--perfettosql-grammar-include', 88 default=os.path.normpath( 89 'src/trace_processor/perfetto_sql/grammar/perfettosql_include.y'), 90 ) 91 parser.add_argument( 92 '--grammar-out', 93 default=os.path.join( 94 os.path.normpath('src/trace_processor/perfetto_sql/grammar/')), 95 ) 96 parser.add_argument( 97 '--sqlite-tokenize', 98 default=os.path.normpath('buildtools/sqlite_src/src/tokenize.c'), 99 ) 100 parser.add_argument( 101 '--sqlite-tokenize-out', 102 default=os.path.join( 103 os.path.normpath( 104 'src/trace_processor/perfetto_sql/tokenizer/tokenize_internal.c') 105 ), 106 ) 107 args = parser.parse_args() 108 109 with tempfile.TemporaryDirectory() as tmp: 110 # Preprocessor grammar 111 subprocess.check_call([ 112 args.clang, 113 os.path.join(args.lemon), '-o', 114 os.path.join(tmp, 'lemon') 115 ]) 116 shutil.copy(args.lemon_template, tmp) 117 subprocess.check_call([ 118 os.path.join(tmp, 'lemon'), 119 args.preprocessor_grammar, 120 '-q', 121 '-l', 122 '-s', 123 ]) 124 125 # PerfettoSQL keywords 126 keywordhash_tmp = os.path.join(tmp, 'mkkeywordhash.c') 127 shutil.copy(args.mkkeywordhash, keywordhash_tmp) 128 129 with open(keywordhash_tmp, "r+") as fp: 130 keyword_source = fp.read() 131 assert keyword_source.find(KEYWORD_END) != -1 132 fp.seek(0) 133 fp.write(keyword_source.replace(KEYWORD_END, KEYWORD_END_REPLACE)) 134 fp.truncate() 135 136 subprocess.check_call([ 137 args.clang, 138 os.path.join(keywordhash_tmp), '-o', 139 os.path.join(tmp, 'mkkeywordhash') 140 ]) 141 keywordhash_res = subprocess.check_output( 142 [os.path.join(tmp, 'mkkeywordhash')]).decode() 143 144 with open(os.path.join(args.grammar_out, "perfettosql_keywordhash.h"), 145 "w") as g: 146 idx = keywordhash_res.find('#define SQLITE_N_KEYWORD') 147 assert idx != -1 148 keywordhash_res = keywordhash_res[0:idx] 149 g.write(KEYWORDHASH_HEADER) 150 g.write(keywordhash_res) 151 152 # PerfettoSQL grammar 153 sqlite_grammar = subprocess.check_output([ 154 os.path.join(tmp, 'lemon'), 155 args.sqlite_grammar, 156 '-g', 157 ]).decode() 158 with open(os.path.join(args.grammar_out, "perfettosql_grammar.y"), 159 "w") as g: 160 with open(args.perfettosql_grammar_include, 'r') as i: 161 g.write(i.read()) 162 g.write(sqlite_grammar) 163 g.write(GRAMMAR_FOOTER) 164 subprocess.check_call([ 165 os.path.join(tmp, 'lemon'), 166 os.path.join(args.grammar_out, "perfettosql_grammar.y"), 167 '-q', 168 '-l', 169 '-s', 170 ]) 171 172 copy_tokenizer(args) 173 174 return 0 175 176 177if __name__ == '__main__': 178 sys.exit(main()) 179