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