1# Copyright (C) 2014-2016 Intel Corporation. All Rights Reserved. 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the "Software"), 5# to deal in the Software without restriction, including without limitation 6# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7# and/or sell copies of the Software, and to permit persons to whom the 8# Software is furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice (including the next 11# paragraph) shall be included in all copies or substantial portions of the 12# Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21 22# Python source 23import os 24import sys 25import re 26from gen_common import * 27 28def parse_event_fields(lines, idx, event_dict): 29 """ 30 Parses lines from a proto file that contain an event definition and stores it in event_dict 31 """ 32 fields = [] 33 end_of_event = False 34 35 # record all fields in event definition. 36 # note: we don't check if there's a leading brace. 37 while not end_of_event and idx < len(lines): 38 line = lines[idx].rstrip() 39 idx += 1 40 41 # ex 1: uint32_t numSampleCLZExecuted; // number of sample_cl_z instructions executed 42 # ex 2: char reason[256]; // size of reason 43 match = re.match(r'^(\s*)([\w\*]+)(\s+)([\w]+)(\[\d+\])*;\s*(\/\/.*)*$', line) 44 # group 1 - 45 # group 2 type 46 # group 3 - 47 # group 4 name 48 # group 5 [array size] 49 # group 6 //comment 50 51 if match: 52 field = { 53 "type": match.group(2), 54 "name": match.group(4), 55 "size": int(match.group(5)[1:-1]) if match.group(5) else 1, 56 "desc": match.group(6)[2:].strip() if match.group(6) else "", 57 } 58 fields.append(field) 59 60 end_of_event = re.match(r'(\s*)};', line) 61 62 event_dict['fields'] = fields 63 event_dict['num_fields'] = len(fields) 64 65 return idx 66 67def parse_enums(lines, idx, event_dict): 68 """ 69 Parses lines from a proto file that contain an enum definition and stores it in event_dict 70 """ 71 enum_names = [] 72 end_of_enum = False 73 74 # record all enum values in enumeration 75 # note: we don't check if there's a leading brace. 76 while not end_of_enum and idx < len(lines): 77 line = lines[idx].rstrip() 78 idx += 1 79 80 preprocessor = re.search(r'#if|#endif', line) 81 82 if not preprocessor: 83 enum = re.match(r'(\s*)(\w+)(\s*)', line) 84 85 if enum: 86 enum_names.append(line) 87 88 end_of_enum = re.match(r'(\s*)};', line) 89 90 event_dict['names'] = enum_names 91 return idx 92 93def parse_protos(files, verbose=False): 94 """ 95 Parses a proto file and returns a dictionary of event definitions 96 """ 97 98 # Protos structure: 99 # 100 # { 101 # "events": { 102 # "defs": { // dict of event definitions where keys are 'group_name::event_name" 103 # ..., 104 # "ApiStat::DrawInfoEvent": { 105 # "id": 3, 106 # "group": "ApiStat", 107 # "name": "DrawInfoEvent", // name of event without 'group_name::' prefix 108 # "desc": "", 109 # "fields": [ 110 # { 111 # "type": "uint32_t", 112 # "name": "drawId", 113 # "size": 1, 114 # "desc": "", 115 # }, 116 # ... 117 # ] 118 # }, 119 # ... 120 # }, 121 # "groups": { // dict of groups with lists of event keys 122 # "ApiStat": [ 123 # "ApiStat::DispatchEvent", 124 # "ApiStat::DrawInfoEvent", 125 # ... 126 # ], 127 # "Framework": [ 128 # "Framework::ThreadStartApiEvent", 129 # "Framework::ThreadStartWorkerEvent", 130 # ... 131 # ], 132 # ... 133 # }, 134 # "map": { // map of event ids to match archrast output to event key 135 # "1": "Framework::ThreadStartApiEvent", 136 # "2": "Framework::ThreadStartWorkerEvent", 137 # "3": "ApiStat::DrawInfoEvent", 138 # ... 139 # } 140 # }, 141 # "enums": { ... } // enums follow similar defs, map (groups?) structure 142 # } 143 144 protos = { 145 'events': { 146 'defs': {}, # event dictionary containing events with their fields 147 'map': {}, # dictionary to map event ids to event names 148 'groups': {} # event keys stored by groups 149 }, 150 'enums': { 151 'defs': {}, 152 'map': {} 153 } 154 } 155 156 event_id = 0 157 enum_id = 0 158 159 if type(files) is not list: 160 files = [files] 161 162 for filename in files: 163 if verbose: 164 print("Parsing proto file: %s" % os.path.normpath(filename)) 165 166 with open(filename, 'r') as f: 167 lines = f.readlines() 168 in_brief = False 169 brief = [] 170 idx = 0 171 while idx < len(lines): 172 line = lines[idx].strip() 173 idx += 1 174 175 # If currently processing a brief, keep processing or change state 176 if in_brief: 177 match = re.match(r'^\s*\/\/\/\s*(.*)$', line) # i.e. "/// more event desc..." 178 if match: 179 brief.append(match.group(1).strip()) 180 continue 181 else: 182 in_brief = False 183 184 # Match event/enum brief 185 match = re.match(r'^\s*\/\/\/\s*@(brief|breif)\s*(.*)$', line) # i.e. "///@brief My event desc..." 186 if match: 187 in_brief = True 188 brief.append(match.group(2).strip()) 189 continue 190 191 # Match event definition 192 match = re.match(r'event(\s*)(((\w*)::){0,1}(\w+))', line) # i.e. "event SWTag::CounterEvent" 193 if match: 194 event_id += 1 195 196 # Parse event attributes 197 event_key = match.group(2) # i.e. SWTag::CounterEvent 198 event_group = match.group(4) if match.group(4) else "" # i.e. SWTag 199 event_name = match.group(5) # i.e. CounterEvent 200 201 # Define event attributes 202 event = { 203 'id': event_id, 204 'group': event_group, 205 'name': event_name, 206 'desc': ' '.join(brief) 207 } 208 # Add period at end of event desc if necessary 209 if event["desc"] and event["desc"][-1] != '.': 210 event["desc"] += '.' 211 212 # Reset brief 213 brief = [] 214 215 # Now add event fields 216 idx = parse_event_fields(lines, idx, event) 217 218 # Register event and mapping 219 protos['events']['defs'][event_key] = event 220 protos['events']['map'][event_id] = event_key 221 222 continue 223 224 # Match enum definition 225 match = re.match(r'enum(\s*)(\w+)', line) 226 if match: 227 enum_id += 1 228 229 # Parse enum attributes 230 enum_name = match.group(2) 231 232 # Define enum attr 233 enum = { 234 'name': enum_name, 235 'desc': ' '.join(brief) 236 } 237 # Add period at end of event desc if necessary 238 if enum["desc"] and enum["desc"][-1] != '.': 239 enum["desc"] += '.' 240 241 # Reset brief 242 brief = [] 243 244 # Now add enum fields 245 idx = parse_enums(lines, idx, enum) 246 247 # Register enum and mapping 248 protos['enums']['defs'][enum_name] = enum 249 protos['enums']['map'][enum_id] = enum_name 250 251 continue 252 253 # Sort and group events 254 event_groups = protos['events']['groups'] 255 for key in sorted(protos['events']['defs']): 256 group = protos['events']['defs'][key]['group'] 257 if group not in event_groups: 258 event_groups[group] = [] 259 event_groups[group].append(key) 260 261 return protos 262 263 264def main(): 265 266 # Parse args... 267 parser = ArgumentParser() 268 parser.add_argument("--proto", "-p", dest="protos", nargs='+', help="Path to all proto file(s) to process. Accepts one or more paths (i.e. events.proto and events_private.proto)", required=True) 269 parser.add_argument("--output-dir", help="Output dir (defaults to ./codegen). Will create folder if it does not exist.", required=False, default="codegen") 270 parser.add_argument("--verbose", "-v", help="Verbose", action="store_true") 271 args = parser.parse_args() 272 273 if not os.path.exists(args.output_dir): 274 MakeDir(args.output_dir) 275 276 for f in args.protos: 277 if not os.path.exists(f): 278 print('Error: Could not find proto file %s' % f, file=sys.stderr) 279 return 1 280 281 # Parse each proto file and add to protos container 282 protos = parse_protos(args.protos, args.verbose) 283 284 files = [ 285 ["gen_ar_event.hpp", ""], 286 ["gen_ar_event.cpp", ""], 287 ["gen_ar_eventhandler.hpp", "gen_ar_event.hpp"], 288 ["gen_ar_eventhandlerfile.hpp", "gen_ar_eventhandler.hpp"] 289 ] 290 291 rval = 0 292 293 try: 294 # Delete existing files 295 for f in files: 296 filename = f[0] 297 output_fullpath = os.path.join(args.output_dir, filename) 298 if os.path.exists(output_fullpath): 299 if args.verbose: 300 print("Deleting existing file: %s" % output_fullpath) 301 os.remove(output_fullpath) 302 303 # Generate files from templates 304 print("Generating c++ from proto files...") 305 for f in files: 306 filename = f[0] 307 event_header = f[1] 308 curdir = os.path.dirname(os.path.abspath(__file__)) 309 template_file = os.path.join(curdir, 'templates', filename) 310 output_fullpath = os.path.join(args.output_dir, filename) 311 312 if args.verbose: 313 print("Generating: %s" % output_fullpath) 314 MakoTemplateWriter.to_file(template_file, output_fullpath, 315 cmdline=sys.argv, 316 filename=filename, 317 protos=protos, 318 event_header=event_header) 319 320 except Exception as e: 321 print(e) 322 rval = 1 323 324 return rval 325 326if __name__ == '__main__': 327 sys.exit(main()) 328