1#!/usr/bin/env python3 2# 3# © 2021 Qualcomm Innovation Center, Inc. All rights reserved. 4# 5# 2019 Cog Systems Pty Ltd. 6# 7# SPDX-License-Identifier: BSD-3-Clause 8 9import argparse 10import os 11import sys 12import logging 13import subprocess 14import inspect 15import pickle 16 17 18if __name__ == '__main__' and __package__ is None: 19 sys.path.append(os.path.dirname(os.path.dirname(__file__))) 20 from utils import genfile 21else: 22 from ..utils import genfile 23 24 25logger = logging.getLogger(__name__) 26 27 28def main(): 29 logging.basicConfig( 30 level=logging.INFO, 31 format="%(message)s", 32 ) 33 __loc__ = os.path.relpath(os.path.realpath( 34 os.path.dirname(os.path.join(os.getcwd(), os.path.dirname(__file__))))) 35 36 args = argparse.ArgumentParser() 37 38 mode_args = args.add_mutually_exclusive_group(required=True) 39 mode_args.add_argument('-t', '--template', 40 type=argparse.FileType('r', encoding='utf-8'), 41 help="Template file used to generate output") 42 mode_args.add_argument('--dump-tree', action='store_true', 43 help="Print the parse tree and exit") 44 mode_args.add_argument('-P', '--dump-pickle', 45 type=genfile.GenFileType('wb'), 46 help="Dump the IR to a Python pickle") 47 48 args.add_argument('-m', '--module', default=None, 49 help="Constrain output to a particular module") 50 args.add_argument('-I', '--extra-include', action='append', default=[], 51 help="Extra headers to include") 52 args.add_argument('-d', "--deps", type=genfile.GenFileType('w'), 53 help="Write implicit dependencies to Makefile", 54 default=None) 55 args.add_argument('-o', '--output', type=genfile.GenFileType('w'), 56 default=sys.stdout, help="Write output to file") 57 args.add_argument("-f", "--formatter", 58 help="specify clang-format to format the code") 59 args.add_argument('-p', '--load-pickle', type=argparse.FileType('rb'), 60 help="Load the IR from a Python pickle") 61 args.add_argument('input', metavar='INPUT', nargs='*', 62 type=argparse.FileType('r', encoding='utf-8'), 63 help="Event DSL files to process") 64 options = args.parse_args() 65 66 if options.input and options.load_pickle: 67 logger.error("Cannot specify both inputs and --load-pickle") 68 args.print_usage() 69 sys.exit(1) 70 elif options.input: 71 from lark import Lark, Visitor 72 from parser import TransformToIR 73 74 grammar_file = os.path.join(__loc__, 'grammars', 'events_dsl.lark') 75 parser = Lark.open(grammar_file, parser='lalr', start='start', 76 propagate_positions=True) 77 78 modules = {} 79 events = {} 80 transformer = TransformToIR(modules, events) 81 82 for f in options.input: 83 tree = parser.parse(f.read()) 84 85 class FilenameVisitor(Visitor): 86 def __init__(self, filename): 87 self.filename = filename 88 89 def __default__(self, tree): 90 tree.meta.filename = self.filename 91 92 FilenameVisitor(f.name).visit(tree) 93 if options.dump_tree: 94 print(tree.pretty(), file=options.output) 95 transformer.transform(tree) 96 97 if options.dump_tree: 98 return 0 99 100 errors = transformer.errors 101 for m in modules.values(): 102 errors += m.resolve(events) 103 104 for m in modules.values(): 105 errors += m.finalise() 106 107 if errors: 108 logger.error("Found %d errors, exiting...", errors) 109 sys.exit(1) 110 elif options.load_pickle: 111 modules = pickle.load(options.load_pickle) 112 else: 113 logger.error("Must specify inputs or --load-pickle") 114 args.print_usage() 115 sys.exit(1) 116 117 if options.dump_pickle: 118 pickle.dump(modules, options.dump_pickle, protocol=-1) 119 else: 120 from Cheetah.Template import Template 121 122 try: 123 module = modules[options.module] 124 except KeyError: 125 logger.error("Specified module '%s' is unknown", options.module) 126 sys.exit(1) 127 128 ns = [module, {'extra_includes': options.extra_include}] 129 template = Template(file=options.template, searchList=ns) 130 131 result = str(template) 132 if options.formatter: 133 ret = subprocess.run([options.formatter], 134 input=result.encode("utf-8"), 135 stdout=subprocess.PIPE) 136 result = ret.stdout.decode("utf-8") 137 if ret.returncode != 0: 138 logger.error("Error formatting output", result) 139 sys.exit(1) 140 141 options.output.write(result) 142 143 if options.deps is not None: 144 deps = set() 145 if options.input: 146 deps.add(grammar_file) 147 for m in sys.modules.values(): 148 try: 149 f = inspect.getsourcefile(m) 150 except TypeError: 151 continue 152 if f is None: 153 continue 154 f = os.path.relpath(f) 155 if f.startswith('../'): 156 continue 157 deps.add(f) 158 if options.dump_pickle: 159 out_name = options.dump_pickle.name 160 else: 161 out_name = options.output.name 162 options.deps.write(out_name + ' : ') 163 options.deps.write(' '.join(sorted(deps))) 164 options.deps.write('\n') 165 options.deps.close() 166 167 168if __name__ == '__main__': 169 main() 170