fe92dae0d5ea64cc295caac19ad7470d500d8b37
[sliver-openvswitch.git] / build-aux / extract-ofp-msgs
1 #! /usr/bin/python
2
3 import sys
4 import os.path
5 import re
6
7 line = ""
8
9 OFP10_VERSION = 0x01
10 OFP11_VERSION = 0x02
11 OFP12_VERSION = 0x03
12 OFP13_VERSION = 0x04
13
14 NX_VENDOR_ID = 0x00002320
15
16 OFPT_VENDOR = 4
17 OFPT10_STATS_REQUEST = 16
18 OFPT10_STATS_REPLY = 17
19 OFPT11_STATS_REQUEST = 18
20 OFPT11_STATS_REPLY = 19
21 OFPST_VENDOR = 0xffff
22
23 version_map = {"1.0":     (OFP10_VERSION, OFP10_VERSION),
24                "1.1":     (OFP11_VERSION, OFP11_VERSION),
25                "1.2":     (OFP12_VERSION, OFP12_VERSION),
26                "1.3":     (OFP13_VERSION, OFP13_VERSION),
27                "1.0+":    (OFP10_VERSION, OFP13_VERSION),
28                "1.1+":    (OFP11_VERSION, OFP13_VERSION),
29                "1.2+":    (OFP12_VERSION, OFP13_VERSION),
30                "1.3+":    (OFP13_VERSION, OFP13_VERSION),
31                "1.0-1.1": (OFP10_VERSION, OFP11_VERSION)}
32
33 def get_line():
34     global line
35     global line_number
36     line = input_file.readline()
37     line_number += 1
38     if line == "":
39         fatal("unexpected end of input")
40
41 n_errors = 0
42 def error(msg):
43     global n_errors
44     sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
45     n_errors += 1
46
47 def fatal(msg):
48     error(msg)
49     sys.exit(1)
50
51 def usage():
52     argv0 = os.path.basename(sys.argv[0])
53     print '''\
54 %(argv0)s, for extracting OpenFlow message types from header files
55 usage: %(argv0)s INPUT OUTPUT
56   where INPUT is the name of the input header file
57     and OUTPUT is the output file name.
58 Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
59 only controls #line directives in the output.\
60 ''' % {"argv0": argv0}
61     sys.exit(0)
62
63 def make_sizeof(s):
64     m = re.match(r'(.*) up to (.*)', s)
65     if m:
66         struct, member = m.groups()
67         return "offsetof(%s, %s)" % (struct, member)
68     else:
69         return "sizeof(%s)" % s
70
71 def extract_ofp_msgs(output_file_name):
72     raw_types = []
73
74     all_hdrs = {}
75     all_raws = {}
76     all_raws_order = []
77
78     while True:
79         get_line()
80         if re.match('enum ofpraw', line):
81             break
82
83     while True:
84         get_line()
85         first_line_number = line_number
86         here = '%s:%d' % (file_name, line_number)
87         if (line.startswith('/*')
88             or line.startswith(' *')
89             or not line
90             or line.isspace()):
91             continue
92         elif re.match('}', line):
93             break
94
95         if not line.lstrip().startswith('/*'):
96             fatal("unexpected syntax between ofpraw types")
97
98         comment = line.lstrip()[2:].strip()
99         while not comment.endswith('*/'):
100             get_line()
101             if line.startswith('/*') or not line or line.isspace():
102                 fatal("unexpected syntax within error")
103             comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
104         comment = comment[:-2].rstrip()
105
106         m = re.match(r'([A-Z]+) ([-.+\d]+) \((\d+)\): ([^.]+)\.$', comment)
107         if not m:
108             fatal("unexpected syntax between messages")
109         type_, versions, number, contents = m.groups()
110         number = int(number)
111
112         get_line()
113         m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
114                      line)
115         if not m:
116             fatal("syntax error expecting OFPRAW_ enum")
117         vinfix, name = m.groups()
118         rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
119
120         min_version, max_version = version_map[versions]
121
122         human_name = '%s_%s' % (type_, name)
123         if type_.endswith('ST'):
124             if rawname.endswith('_REQUEST'):
125                 human_name = human_name[:-8] + " request"
126             elif rawname.endswith('_REPLY'):
127                 human_name = human_name[:-6] + " reply"
128             else:
129                 fatal("%s messages are statistics but %s doesn't end "
130                       "in _REQUEST or _REPLY" % (type_, rawname))
131
132         these_hdrs = []
133         for version in range(min_version, max_version + 1):
134             if type_ == 'OFPT':
135                 if number == OFPT_VENDOR:
136                     fatal("OFPT (%d) is used for vendor extensions"
137                           % number)
138                 elif (version == OFP10_VERSION
139                       and (number == OFPT10_STATS_REQUEST
140                            or number == OFPT10_STATS_REPLY)):
141                     fatal("OFPT 1.0 (%d) is used for stats messages"
142                           % number)
143                 elif (version != OFP10_VERSION
144                       and (number == OFPT11_STATS_REQUEST
145                            or number == OFPT11_STATS_REPLY)):
146                     fatal("OFPT 1.1+ (%d) is used for stats messages"
147                           % number)
148                 hdrs = (version, number, 0, 0, 0)
149             elif type_ == 'OFPST' and name.endswith('_REQUEST'):
150                 if version == OFP10_VERSION:
151                     hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
152                 else:
153                     hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
154             elif type_ == 'OFPST' and name.endswith('_REPLY'):
155                 if version == OFP10_VERSION:
156                     hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
157                 else:
158                     hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
159             elif type_ == 'NXT':
160                 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
161             elif type_ == 'NXST' and name.endswith('_REQUEST'):
162                 if version == OFP10_VERSION:
163                     hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
164                             NX_VENDOR_ID, number)
165                 else:
166                     hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
167                             NX_VENDOR_ID, number)
168             elif type_ == 'NXST' and name.endswith('_REPLY'):
169                 if version == OFP10_VERSION:
170                     hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
171                             NX_VENDOR_ID, number)
172                 else:
173                     hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
174                             NX_VENDOR_ID, number)
175             else:
176                 fatal("type '%s' unknown" % type_)
177
178             if hdrs in all_hdrs:
179                 error("Duplicate message definition for %s." % str(hdrs))
180                 sys.stderr.write("%s: Here is the location "
181                                  "of the previous definition.\n"
182                                  % (all_hdrs[hdrs]))
183             all_hdrs[hdrs] = here
184             these_hdrs.append(hdrs)
185
186         extra_multiple = '0'
187         if contents == 'void':
188             min_body = '0'
189         else:
190             min_body_elem = []
191             for c in [s.strip() for s in contents.split(",")]:
192                 if c.endswith('[]'):
193                     if extra_multiple == '0':
194                         extra_multiple = make_sizeof(c[:-2])
195                     else:
196                         error("Cannot have multiple [] elements")
197                 else:
198                     min_body_elem.append(c)
199
200             if min_body_elem:
201                 min_body = " + ".join([make_sizeof(s)
202                                        for s in min_body_elem])
203             else:
204                 if extra_multiple == '0':
205                     error("Must specify contents (use 'void' if empty)")
206                 min_body = 0
207
208         if rawname in all_raws:
209             fatal("%s: Duplicate name" % rawname)
210
211         all_raws[rawname] = {"hdrs": these_hdrs,
212                              "min_version": min_version,
213                              "max_version": max_version,
214                              "min_body": min_body,
215                              "extra_multiple": extra_multiple,
216                              "type": type_,
217                              "human_name": human_name,
218                              "line": first_line_number}
219         all_raws_order.append(rawname)
220
221         continue
222
223     while True:
224         get_line()
225         if re.match('enum ofptype', line):
226             break
227
228     while True:
229         get_line()
230         if re.match(r'\s*/?\*', line) or line.isspace():
231             continue
232         elif re.match('}', line):
233             break
234
235         if not re.match(r'\s*OFPTYPE_.*/\*', line):
236             fatal("unexpected syntax between OFPTYPE_ definitions")
237
238         syntax = line.strip()
239         while not syntax.endswith('*/'):
240             get_line()
241             if not line.strip().startswith('*'):
242                 fatal("unexpected syntax within OFPTYPE_ definition")
243             syntax += ' %s' % line.strip().lstrip('* \t')
244             syntax = syntax.strip()
245
246         m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
247         if not m:
248             fatal("syntax error in OFPTYPE_ definition")
249
250         ofptype, raws_ = m.groups()
251         raws = [s.rstrip('.') for s in raws_.split()]
252         for raw in raws:
253             if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
254                 fatal("%s: invalid OFPRAW_* name syntax" % raw)
255             if raw not in all_raws:
256                 fatal("%s: not a declared OFPRAW_* name" % raw)
257             if "ofptype" in all_raws[raw]:
258                 fatal("%s: already part of %s"
259                       % (raw, all_raws[raw]["ofptype"]))
260             all_raws[raw]["ofptype"] = ofptype
261
262     input_file.close()
263
264     if n_errors:
265         sys.exit(1)
266
267     output = []
268     output.append("/* Generated automatically; do not modify!     "
269                   "-*- buffer-read-only: t -*- */")
270     output.append("")
271
272     for raw in all_raws_order:
273         r = all_raws[raw]
274         output.append("static struct raw_instance %s_instances[] = {"
275                       % raw.lower())
276         for hdrs in r['hdrs']:
277             output.append("    { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
278                           % (hdrs + (raw,)))
279                 
280         output.append("};")
281
282     output.append("")
283
284     output.append("static struct raw_info raw_infos[] = {")
285     for raw in all_raws_order:
286         r = all_raws[raw]
287         if "ofptype" not in r:
288             error("%s: no defined OFPTYPE_" % raw)
289             continue
290         output.append("    {")
291         output.append("        %s_instances," % raw.lower())
292         output.append("        %d, %d," % (r["min_version"], r["max_version"]))
293         output.append("#line %s \"%s\"" % (r["line"], file_name))
294         output.append("        %s," % r["min_body"])
295         output.append("#line %s \"%s\"" % (r["line"], file_name))
296         output.append("        %s," % r["extra_multiple"])
297         output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
298         output.append("        %s," % r["ofptype"])
299         output.append("        \"%s\"," % r["human_name"])
300         output.append("    },")
301
302         if r['type'].endswith("ST"):
303             for hdrs in r['hdrs']:
304                 op_hdrs = list(hdrs)
305                 if hdrs[0] == OFP10_VERSION:
306                     if hdrs[1] == OFPT10_STATS_REQUEST:
307                         op_hdrs[1] = OFPT10_STATS_REPLY
308                     elif hdrs[1] == OFPT10_STATS_REPLY:
309                         op_hdrs[1] = OFPT10_STATS_REQUEST
310                     else:
311                         assert False
312                 else:
313                     if hdrs[1] == OFPT11_STATS_REQUEST:
314                         op_hdrs[1] = OFPT11_STATS_REPLY
315                     elif hdrs[1] == OFPT11_STATS_REPLY:
316                         op_hdrs[1] = OFPT11_STATS_REQUEST
317                     else:
318                         assert False
319                 if tuple(op_hdrs) not in all_hdrs:
320                     if r["human_name"].endswith("request"):
321                         fatal("%s has no corresponding reply"
322                               % r["human_name"])
323                     else:
324                         fatal("%s has no corresponding request"
325                               % r["human_name"])
326     output.append("};")
327
328     if n_errors:
329         sys.exit(1)
330
331     return output
332
333
334 if __name__ == '__main__':
335     if '--help' in sys.argv:
336         usage()
337     elif len(sys.argv) != 3:
338         sys.stderr.write("exactly one non-option arguments required; "
339                          "use --help for help\n")
340         sys.exit(1)
341     else:
342         global file_name
343         global input_file
344         global line_number
345         file_name = sys.argv[1]
346         input_file = open(file_name)
347         line_number = 0
348
349         for line in extract_ofp_msgs(sys.argv[2]):
350             print line
351