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