328 lines
11 KiB
Python
328 lines
11 KiB
Python
# Copyright (C) 2014-2016 Intel Corporation. All Rights Reserved.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
# to deal in the Software without restriction, including without limitation
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice (including the next
|
|
# paragraph) shall be included in all copies or substantial portions of the
|
|
# Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
# IN THE SOFTWARE.
|
|
|
|
# Python source
|
|
import os
|
|
import sys
|
|
import re
|
|
from gen_common import *
|
|
|
|
def parse_event_fields(lines, idx, event_dict):
|
|
"""
|
|
Parses lines from a proto file that contain an event definition and stores it in event_dict
|
|
"""
|
|
fields = []
|
|
end_of_event = False
|
|
|
|
# record all fields in event definition.
|
|
# note: we don't check if there's a leading brace.
|
|
while not end_of_event and idx < len(lines):
|
|
line = lines[idx].rstrip()
|
|
idx += 1
|
|
|
|
# ex 1: uint32_t numSampleCLZExecuted; // number of sample_cl_z instructions executed
|
|
# ex 2: char reason[256]; // size of reason
|
|
match = re.match(r'^(\s*)([\w\*]+)(\s+)([\w]+)(\[\d+\])*;\s*(\/\/.*)*$', line)
|
|
# group 1 -
|
|
# group 2 type
|
|
# group 3 -
|
|
# group 4 name
|
|
# group 5 [array size]
|
|
# group 6 //comment
|
|
|
|
if match:
|
|
field = {
|
|
"type": match.group(2),
|
|
"name": match.group(4),
|
|
"size": int(match.group(5)[1:-1]) if match.group(5) else 1,
|
|
"desc": match.group(6)[2:].strip() if match.group(6) else "",
|
|
}
|
|
fields.append(field)
|
|
|
|
end_of_event = re.match(r'(\s*)};', line)
|
|
|
|
event_dict['fields'] = fields
|
|
event_dict['num_fields'] = len(fields)
|
|
|
|
return idx
|
|
|
|
def parse_enums(lines, idx, event_dict):
|
|
"""
|
|
Parses lines from a proto file that contain an enum definition and stores it in event_dict
|
|
"""
|
|
enum_names = []
|
|
end_of_enum = False
|
|
|
|
# record all enum values in enumeration
|
|
# note: we don't check if there's a leading brace.
|
|
while not end_of_enum and idx < len(lines):
|
|
line = lines[idx].rstrip()
|
|
idx += 1
|
|
|
|
preprocessor = re.search(r'#if|#endif', line)
|
|
|
|
if not preprocessor:
|
|
enum = re.match(r'(\s*)(\w+)(\s*)', line)
|
|
|
|
if enum:
|
|
enum_names.append(line)
|
|
|
|
end_of_enum = re.match(r'(\s*)};', line)
|
|
|
|
event_dict['names'] = enum_names
|
|
return idx
|
|
|
|
def parse_protos(files, verbose=False):
|
|
"""
|
|
Parses a proto file and returns a dictionary of event definitions
|
|
"""
|
|
|
|
# Protos structure:
|
|
#
|
|
# {
|
|
# "events": {
|
|
# "defs": { // dict of event definitions where keys are 'group_name::event_name"
|
|
# ...,
|
|
# "ApiStat::DrawInfoEvent": {
|
|
# "id": 3,
|
|
# "group": "ApiStat",
|
|
# "name": "DrawInfoEvent", // name of event without 'group_name::' prefix
|
|
# "desc": "",
|
|
# "fields": [
|
|
# {
|
|
# "type": "uint32_t",
|
|
# "name": "drawId",
|
|
# "size": 1,
|
|
# "desc": "",
|
|
# },
|
|
# ...
|
|
# ]
|
|
# },
|
|
# ...
|
|
# },
|
|
# "groups": { // dict of groups with lists of event keys
|
|
# "ApiStat": [
|
|
# "ApiStat::DispatchEvent",
|
|
# "ApiStat::DrawInfoEvent",
|
|
# ...
|
|
# ],
|
|
# "Framework": [
|
|
# "Framework::ThreadStartApiEvent",
|
|
# "Framework::ThreadStartWorkerEvent",
|
|
# ...
|
|
# ],
|
|
# ...
|
|
# },
|
|
# "map": { // map of event ids to match archrast output to event key
|
|
# "1": "Framework::ThreadStartApiEvent",
|
|
# "2": "Framework::ThreadStartWorkerEvent",
|
|
# "3": "ApiStat::DrawInfoEvent",
|
|
# ...
|
|
# }
|
|
# },
|
|
# "enums": { ... } // enums follow similar defs, map (groups?) structure
|
|
# }
|
|
|
|
protos = {
|
|
'events': {
|
|
'defs': {}, # event dictionary containing events with their fields
|
|
'map': {}, # dictionary to map event ids to event names
|
|
'groups': {} # event keys stored by groups
|
|
},
|
|
'enums': {
|
|
'defs': {},
|
|
'map': {}
|
|
}
|
|
}
|
|
|
|
event_id = 0
|
|
enum_id = 0
|
|
|
|
if type(files) is not list:
|
|
files = [files]
|
|
|
|
for filename in files:
|
|
if verbose:
|
|
print("Parsing proto file: %s" % os.path.normpath(filename))
|
|
|
|
with open(filename, 'r') as f:
|
|
lines = f.readlines()
|
|
in_brief = False
|
|
brief = []
|
|
idx = 0
|
|
while idx < len(lines):
|
|
line = lines[idx].strip()
|
|
idx += 1
|
|
|
|
# If currently processing a brief, keep processing or change state
|
|
if in_brief:
|
|
match = re.match(r'^\s*\/\/\/\s*(.*)$', line) # i.e. "/// more event desc..."
|
|
if match:
|
|
brief.append(match.group(1).strip())
|
|
continue
|
|
else:
|
|
in_brief = False
|
|
|
|
# Match event/enum brief
|
|
match = re.match(r'^\s*\/\/\/\s*@(brief|breif)\s*(.*)$', line) # i.e. "///@brief My event desc..."
|
|
if match:
|
|
in_brief = True
|
|
brief.append(match.group(2).strip())
|
|
continue
|
|
|
|
# Match event definition
|
|
match = re.match(r'event(\s*)(((\w*)::){0,1}(\w+))', line) # i.e. "event SWTag::CounterEvent"
|
|
if match:
|
|
event_id += 1
|
|
|
|
# Parse event attributes
|
|
event_key = match.group(2) # i.e. SWTag::CounterEvent
|
|
event_group = match.group(4) if match.group(4) else "" # i.e. SWTag
|
|
event_name = match.group(5) # i.e. CounterEvent
|
|
|
|
# Define event attributes
|
|
event = {
|
|
'id': event_id,
|
|
'group': event_group,
|
|
'name': event_name,
|
|
'desc': ' '.join(brief)
|
|
}
|
|
# Add period at end of event desc if necessary
|
|
if event["desc"] and event["desc"][-1] != '.':
|
|
event["desc"] += '.'
|
|
|
|
# Reset brief
|
|
brief = []
|
|
|
|
# Now add event fields
|
|
idx = parse_event_fields(lines, idx, event)
|
|
|
|
# Register event and mapping
|
|
protos['events']['defs'][event_key] = event
|
|
protos['events']['map'][event_id] = event_key
|
|
|
|
continue
|
|
|
|
# Match enum definition
|
|
match = re.match(r'enum(\s*)(\w+)', line)
|
|
if match:
|
|
enum_id += 1
|
|
|
|
# Parse enum attributes
|
|
enum_name = match.group(2)
|
|
|
|
# Define enum attr
|
|
enum = {
|
|
'name': enum_name,
|
|
'desc': ' '.join(brief)
|
|
}
|
|
# Add period at end of event desc if necessary
|
|
if enum["desc"] and enum["desc"][-1] != '.':
|
|
enum["desc"] += '.'
|
|
|
|
# Reset brief
|
|
brief = []
|
|
|
|
# Now add enum fields
|
|
idx = parse_enums(lines, idx, enum)
|
|
|
|
# Register enum and mapping
|
|
protos['enums']['defs'][enum_name] = enum
|
|
protos['enums']['map'][enum_id] = enum_name
|
|
|
|
continue
|
|
|
|
# Sort and group events
|
|
event_groups = protos['events']['groups']
|
|
for key in sorted(protos['events']['defs']):
|
|
group = protos['events']['defs'][key]['group']
|
|
if group not in event_groups:
|
|
event_groups[group] = []
|
|
event_groups[group].append(key)
|
|
|
|
return protos
|
|
|
|
|
|
def main():
|
|
|
|
# Parse args...
|
|
parser = ArgumentParser()
|
|
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)
|
|
parser.add_argument("--output-dir", help="Output dir (defaults to ./codegen). Will create folder if it does not exist.", required=False, default="codegen")
|
|
parser.add_argument("--verbose", "-v", help="Verbose", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
if not os.path.exists(args.output_dir):
|
|
MakeDir(args.output_dir)
|
|
|
|
for f in args.protos:
|
|
if not os.path.exists(f):
|
|
print('Error: Could not find proto file %s' % f, file=sys.stderr)
|
|
return 1
|
|
|
|
# Parse each proto file and add to protos container
|
|
protos = parse_protos(args.protos, args.verbose)
|
|
|
|
files = [
|
|
["gen_ar_event.hpp", ""],
|
|
["gen_ar_event.cpp", ""],
|
|
["gen_ar_eventhandler.hpp", "gen_ar_event.hpp"],
|
|
["gen_ar_eventhandlerfile.hpp", "gen_ar_eventhandler.hpp"]
|
|
]
|
|
|
|
rval = 0
|
|
|
|
try:
|
|
# Delete existing files
|
|
for f in files:
|
|
filename = f[0]
|
|
output_fullpath = os.path.join(args.output_dir, filename)
|
|
if os.path.exists(output_fullpath):
|
|
if args.verbose:
|
|
print("Deleting existing file: %s" % output_fullpath)
|
|
os.remove(output_fullpath)
|
|
|
|
# Generate files from templates
|
|
print("Generating c++ from proto files...")
|
|
for f in files:
|
|
filename = f[0]
|
|
event_header = f[1]
|
|
curdir = os.path.dirname(os.path.abspath(__file__))
|
|
template_file = os.path.join(curdir, 'templates', filename)
|
|
output_fullpath = os.path.join(args.output_dir, filename)
|
|
|
|
if args.verbose:
|
|
print("Generating: %s" % output_fullpath)
|
|
MakoTemplateWriter.to_file(template_file, output_fullpath,
|
|
cmdline=sys.argv,
|
|
filename=filename,
|
|
protos=protos,
|
|
event_header=event_header)
|
|
|
|
except Exception as e:
|
|
print(e)
|
|
rval = 1
|
|
|
|
return rval
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|