• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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