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