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        match = re.match(r'(\s*)([\w\*]+)(\s+)(counter\s+)*([\w]+)(\[\d+\])*', line)
43
44        if match:
45            field = {
46                "type": match.group(2),
47                "name": match.group(5),
48                "size": int(match.group(6)[1:-1]) if match.group(6) else 1,
49                "counter": True if match.group(4) else False
50            }
51            fields.append(field)
52
53        end_of_event = re.match(r'(\s*)};', line)
54
55    event_dict['fields'] = fields
56    event_dict['num_fields'] = len(fields)
57
58    return idx
59
60def parse_enums(lines, idx, event_dict):
61    """
62        Parses lines from a proto file that contain an enum definition and stores it in event_dict
63    """
64    enum_names = []
65    end_of_enum = False
66
67    # record all enum values in enumeration
68    # note: we don't check if there's a leading brace.
69    while not end_of_enum and idx < len(lines):
70        line = lines[idx].rstrip()
71        idx += 1
72
73        preprocessor = re.search(r'#if|#endif', line)
74
75        if not preprocessor:
76            enum = re.match(r'(\s*)(\w+)(\s*)', line)
77
78            if enum:
79                enum_names.append(line)
80
81            end_of_enum = re.match(r'(\s*)};', line)
82
83    event_dict['names'] = enum_names
84    return idx
85
86def parse_protos(files, verbose=False):
87    """
88        Parses a proto file and returns a dictionary of event definitions
89    """
90    protos = {}
91    protos['events'] = {}       # event dictionary containing events with their fields
92    protos['event_names'] = []  # needed to keep events in order parsed. dict is not ordered.
93    protos['event_map'] = {}    # dictionary to map event ids to event names
94    protos['enums'] = {}
95    protos['enum_names'] = []
96
97    eventId = 0
98
99    if type(files) is not list:
100        files = [files]
101
102    for filename in files:
103        if verbose:
104            print("Parsing proto file: %s" % os.path.normpath(filename))
105
106        with open(filename, 'r') as f:
107            lines=f.readlines()
108
109            idx = 0
110
111            raw_text = []
112            while idx < len(lines):
113                line = lines[idx].rstrip()
114                idx += 1
115
116                # search for event definitions.
117                match = re.match(r'(\s*)event(\s*)(\w+)', line)
118
119                if match:
120                    eventId += 1
121                    event_name = match.group(3)
122                    protos["event_names"].append(event_name)
123
124                    protos["events"][event_name] = {}
125                    protos["events"][event_name]["event_id"] = eventId
126                    protos["event_map"][eventId] = event_name
127                    idx = parse_event_fields(lines, idx, protos["events"][event_name])
128
129                # search for enums.
130                match = re.match(r'(\s*)enum(\s*)(\w+)', line)
131
132                if match:
133                    enum_name = match.group(3)
134                    protos["enum_names"].append(enum_name)
135
136                    protos["enums"][enum_name] = {}
137                    idx = parse_enums(lines, idx, protos["enums"][enum_name])
138    return protos
139
140
141def main():
142
143    # Parse args...
144    parser = ArgumentParser()
145    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)
146    parser.add_argument("--output-dir", help="Output dir (defaults to ./codegen). Will create folder if it does not exist.", required=False, default="codegen")
147    parser.add_argument("--verbose", "-v", help="Verbose", action="store_true")
148    args = parser.parse_args()
149
150    if not os.path.exists(args.output_dir):
151        MakeDir(args.output_dir)
152
153    for f in args.protos:
154        if not os.path.exists(f):
155            print('Error: Could not find proto file %s' % f, file=sys.stderr)
156            return 1
157
158    # Parse each proto file and add to protos container
159    protos = parse_protos(args.protos, args.verbose)
160
161    files = [
162        ["gen_ar_event.hpp", ""],
163        ["gen_ar_event.cpp", ""],
164        ["gen_ar_eventhandler.hpp", "gen_ar_event.hpp"],
165        ["gen_ar_eventhandlerfile.hpp", "gen_ar_eventhandler.hpp"]
166    ]
167
168    rval = 0
169
170    try:
171        # Delete existing files
172        for f in files:
173            filename = f[0]
174            output_fullpath = os.path.join(args.output_dir, filename)
175            if os.path.exists(output_fullpath):
176                if args.verbose:
177                    print("Deleting existing file: %s" % output_fullpath)
178                os.remove(output_fullpath)
179
180        # Generate files from templates
181        print("Generating c++ from proto files...")
182        for f in files:
183            filename = f[0]
184            event_header = f[1]
185            curdir = os.path.dirname(os.path.abspath(__file__))
186            template_file = os.path.join(curdir, 'templates', filename)
187            output_fullpath = os.path.join(args.output_dir, filename)
188
189            if args.verbose:
190                print("Generating: %s" % output_fullpath)
191            MakoTemplateWriter.to_file(template_file, output_fullpath,
192                    cmdline=sys.argv,
193                    filename=filename,
194                    protos=protos,
195                    event_header=event_header)
196
197    except Exception as e:
198        print(e)
199        rval = 1
200
201    return rval
202
203if __name__ == '__main__':
204    sys.exit(main())
205