blob: 7b78f63689506265b5a6882b868c0b463953d318 [file] [log] [blame]
YAMAMOTO Takashi6df0abb2013-07-01 17:24:04 +09001#!/usr/bin/env python
Rich Lanea06d0c32013-03-25 08:52:03 -07002# Copyright 2013, Big Switch Networks, Inc.
3#
4# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
5# the following special exception:
6#
7# LOXI Exception
8#
9# As a special exception to the terms of the EPL, you may distribute libraries
10# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
11# that copyright and licensing notices generated by LoxiGen are not altered or removed
12# from the LoxiGen Libraries and the notice provided below is (i) included in
13# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
14# documentation for the LoxiGen Libraries, if distributed in binary form.
15#
16# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
17#
18# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
19# a copy of the EPL at:
20#
21# http://www.eclipse.org/legal/epl-v10.html
22#
23# Unless required by applicable law or agreed to in writing, software
24# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26# EPL for the specific language governing permissions and limitations
27# under the EPL.
28
29"""
30@brief
31Process openflow header files to create language specific LOXI interfaces
32
33First cut at simple python script for processing input files
34
35Internal notes
36
37An input file for each supported OpenFlow version is passed in
38on the command line.
39
40Expected input file format:
41
42These will probably be collapsed into a python dict or something
43
44The first line has the ofC version identifier and nothing else
45The second line has the openflow wire protocol value and nothing else
46
47The main content is struct elements for each OF recognized class.
48These are taken from current versions of openflow.h but are modified
49a bit. See Overview for more information.
50
Andreas Wundsam53256162013-05-02 14:05:53 -070051Class canonical form: A list of entries, each of which is a
Rich Lanea06d0c32013-03-25 08:52:03 -070052pair "type, name;". The exception is when type is the keyword
53'list' in which the syntax is "list(type) name;".
54
55From this, internal representations are generated: For each
56version, a dict indexed by class name. One element (members) is
57an array giving the member name and type. From this, wire offsets
58can be calculated.
59
60
61@fixme Clean up the lang module architecture. It should provide a
62list of files that it wants to generate and maps to the filenames,
Andreas Wundsam53256162013-05-02 14:05:53 -070063subdirectory names and generation functions. It should also be
64defined as a class, probably with the constructor taking the
Rich Lanea06d0c32013-03-25 08:52:03 -070065language target.
66
67@fixme Clean up global data structures such as versions and of_g
68structures. They should probably be a class or classes as well.
69
70"""
71
72import sys
73
74import re
75import string
76import os
77import glob
78import copy
Rich Lanec9e111d2013-05-09 16:10:30 -070079import collections
Rich Lanea06d0c32013-03-25 08:52:03 -070080import of_g
Rich Lanea06d0c32013-03-25 08:52:03 -070081import loxi_front_end.type_maps as type_maps
82import loxi_utils.loxi_utils as loxi_utils
Rich Lanea06d0c32013-03-25 08:52:03 -070083import loxi_front_end.c_parse_utils as c_parse_utils
84import loxi_front_end.identifiers as identifiers
85import pyparsing
86import loxi_front_end.parser as parser
Rich Lane38388e62013-04-08 14:09:46 -070087import loxi_front_end.translation as translation
Rich Laned47e5a22013-05-09 14:21:16 -070088import loxi_front_end.frontend as frontend
Rich Lane4d9f0f62013-05-09 15:50:57 -070089from loxi_ir import *
Rich Lanea06d0c32013-03-25 08:52:03 -070090from generic_utils import *
91
92root_dir = os.path.dirname(os.path.realpath(__file__))
93
94# TODO: Put these in a class so they get documented
95
96## Dict indexed by version giving all info related to version
97#
98# This is local; after processing, the information is stored in
99# of_g variables.
100versions = {}
101
102def config_sanity_check():
103 """
104 Check the configuration for basic consistency
105
106 @fixme Needs update for generic language support
107 """
108
109 rv = True
110 # For now, only "error" supported for get returns
111 if config_check("copy_semantics") != "read":
112 debug("Only 'read' is supported for copy_semantics");
Andreas Wundsam53256162013-05-02 14:05:53 -0700113 rv = False
Rich Lanea06d0c32013-03-25 08:52:03 -0700114 if config_check("get_returns") != "error":
115 debug("Only 'error' is supported for get-accessor return types\m");
Andreas Wundsam53256162013-05-02 14:05:53 -0700116 rv = False
Rich Lanea06d0c32013-03-25 08:52:03 -0700117 if not config_check("use_fn_ptrs") and not config_check("gen_unified_fns"):
118 debug("Must have gen_fn_ptrs and/or gen_unified_fns set in config")
119 rv = False
120 if config_check("use_obj_id"):
121 debug("use_obj_id is set but not yet supported (change \
122config_sanity_check if it is)")
123 rv = False
124 if config_check("gen_unified_macros") and config_check("gen_unified_fns") \
125 and config_check("gen_unified_macro_lower"):
126 debug("Conflict: Cannot generate unified functions and lower case \
127unified macros")
128 rv = False
Andreas Wundsam53256162013-05-02 14:05:53 -0700129
Rich Lanea06d0c32013-03-25 08:52:03 -0700130 return rv
131
132def add_class(wire_version, cls, members):
133 """
Andreas Wundsam53256162013-05-02 14:05:53 -0700134 Process a class for the given version and update the unified
Rich Lanea06d0c32013-03-25 08:52:03 -0700135 list of classes as needed.
136
137 @param wire_version The wire version for this class defn
138 @param cls The name of the class being added
139 @param members The list of members with offsets calculated
140 """
141 memid = 0
142
143 sig = loxi_utils.class_signature(members)
144 if cls in of_g.unified:
145 uc = of_g.unified[cls]
146 if wire_version in uc:
147 debug("Error adding %s to unified. Wire ver %d exists" %
148 (cls, wire_version))
149 sys.exit(1)
150 uc[wire_version] = {}
151 # Check for a matching signature
152 for wver in uc:
153 if type(wver) != type(0): continue
154 if wver == wire_version: continue
155 if not "use_version" in uc[wver]:
156 if sig == loxi_utils.class_signature(uc[wver]["members"]):
Andreas Wundsam53256162013-05-02 14:05:53 -0700157 log("Matched %s, ver %d to ver %d" %
Rich Lanea06d0c32013-03-25 08:52:03 -0700158 (cls, wire_version, wver))
159 # have a match with existing version
160 uc[wire_version]["use_version"] = wver
161 # What else to do?
162 return
163 else: # Haven't seen this entry before
164 log("Adding %s to unified list, ver %d" % (cls, wire_version))
165 of_g.unified[cls] = dict(union={})
166 uc = of_g.unified[cls]
167
168 # At this point, need to add members for this version
169 uc[wire_version] = dict(members = members)
170
171 # Per member processing:
172 # Add to union list (I'm sure there's a better way)
173 # Check if it's a list
174 union = uc["union"]
175 if not cls in of_g.ordered_members:
176 of_g.ordered_members[cls] = []
177 for member in members:
178 m_name = member["name"]
179 m_type = member["m_type"]
180 if m_name.find("pad") == 0:
181 continue
182 if m_name in union:
183 if not m_type == union[m_name]["m_type"]:
184 debug("ERROR: CLASS: %s. VERSION %d. MEMBER: %s. TYPE: %s" %
185 (cls, wire_version, m_name, m_type))
186 debug(" Type conflict adding member to unified set.")
187 debug(" Current union[%s]:" % m_name)
188 debug(union[m_name])
189 sys.exit(1)
190 else:
191 union[m_name] = dict(m_type=m_type, memid=memid)
192 memid += 1
193 if not m_name in of_g.ordered_members[cls]:
194 of_g.ordered_members[cls].append(m_name)
195
196def update_offset(cls, wire_version, name, offset, m_type):
197 """
198 Update (and return) the offset based on type.
199 @param cls The parent class
200 @param wire_version The wire version being processed
201 @param name The name of the data member
202 @param offset The current offset
203 @param m_type The type declaration being processed
204 @returns A pair (next_offset, len_update) next_offset is the new offset
205 of the next object or -1 if this is a var-length object. len_update
206 is the increment that should be added to the length. Note that (for
207 of_match_v3) it is variable length, but it adds 8 bytes to the fixed
208 length of the object
209 If offset is already -1, do not update
210 Otherwise map to base type and count and update (if possible)
211 """
212 if offset < 0: # Don't update offset once set to -1
213 return offset, 0
214
215 count, base_type = c_parse_utils.type_dec_to_count_base(m_type)
216
217 len_update = 0
218 if base_type in of_g.of_mixed_types:
219 base_type = of_g.of_mixed_types[base_type][wire_version]
220
221 base_class = base_type[:-2]
222 if (base_class, wire_version) in of_g.is_fixed_length:
223 bytes = of_g.base_length[(base_class, wire_version)]
224 else:
225 if base_type == "of_match_v3_t":
226 # This is a special case: it has non-zero min length
227 # but is variable length
228 bytes = -1
229 len_update = 8
230 elif base_type in of_g.of_base_types:
231 bytes = of_g.of_base_types[base_type]["bytes"]
232 else:
233 print "UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type)
234 log("UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type))
235 bytes = -1
236
237 # If bytes
238 if bytes > 0:
239 len_update = count * bytes
240
241 if bytes == -1:
242 return -1, len_update
243
244 return offset + (count * bytes), len_update
245
246def calculate_offsets_and_lengths(ordered_classes, classes, wire_version):
247 """
248 Generate the offsets for fixed offset class members
249 Also calculate the class_sizes when possible.
250
251 @param classes The classes to process
252 @param wire_version The wire version for this set of classes
253
254 Updates global variables
255 """
256
257 lists = set()
258
259 # Generate offsets
260 for cls in ordered_classes:
261 fixed_offset = 0 # The last "good" offset seen
262 offset = 0
263 last_offset = 0
264 last_name = "-"
265 for member in classes[cls]:
266 m_type = member["m_type"]
267 name = member["name"]
268 if last_offset == -1:
269 if name == "pad":
270 log("Skipping pad for special offset for %s" % cls)
271 else:
Andreas Wundsam53256162013-05-02 14:05:53 -0700272 log("SPECIAL OFS: Member %s (prev %s), class %s ver %d" %
Rich Lanea06d0c32013-03-25 08:52:03 -0700273 (name, last_name, cls, wire_version))
274 if (((cls, name) in of_g.special_offsets) and
275 (of_g.special_offsets[(cls, name)] != last_name)):
276 debug("ERROR: special offset prev name changed")
277 debug(" cls %s. name %s. version %d. was %s. now %s" %
Andreas Wundsam53256162013-05-02 14:05:53 -0700278 cls, name, wire_version,
Rich Lanea06d0c32013-03-25 08:52:03 -0700279 of_g.special_offsets[(cls, name)], last_name)
280 sys.exit(1)
281 of_g.special_offsets[(cls, name)] = last_name
282
283 member["offset"] = offset
284 if m_type.find("list(") == 0:
285 (list_name, base_type) = loxi_utils.list_name_extract(m_type)
286 lists.add(list_name)
287 member["m_type"] = list_name + "_t"
288 offset = -1
289 elif m_type.find("struct") == 0:
290 debug("ERROR found struct: %s.%s " % (cls, name))
291 sys.exit(1)
292 elif m_type == "octets":
293 log("offset gen skipping octets: %s.%s " % (cls, name))
294 offset = -1
295 else:
Andreas Wundsam53256162013-05-02 14:05:53 -0700296 offset, len_update = update_offset(cls, wire_version, name,
Rich Lanea06d0c32013-03-25 08:52:03 -0700297 offset, m_type)
298 if offset != -1:
299 fixed_offset = offset
300 else:
301 fixed_offset += len_update
Andreas Wundsam53256162013-05-02 14:05:53 -0700302 log("offset is -1 for %s.%s version %d " %
Rich Lanea06d0c32013-03-25 08:52:03 -0700303 (cls, name, wire_version))
304 last_offset = offset
305 last_name = name
306 of_g.base_length[(cls, wire_version)] = fixed_offset
307 if (offset != -1):
308 of_g.is_fixed_length.add((cls, wire_version))
309 for list_type in lists:
310 classes[list_type] = []
311 of_g.ordered_classes[wire_version].append(list_type)
312 of_g.base_length[(list_type, wire_version)] = 0
313
314def process_input_file(filename):
315 """
316 Process an input file
317
Rich Laned47e5a22013-05-09 14:21:16 -0700318 Does not modify global state.
319
Rich Lanea06d0c32013-03-25 08:52:03 -0700320 @param filename The input filename
321
Rich Laned47e5a22013-05-09 14:21:16 -0700322 @returns An OFInput object
Rich Lanea06d0c32013-03-25 08:52:03 -0700323 """
324
325 # Parse the input file
326 try:
Rich Laned47e5a22013-05-09 14:21:16 -0700327 with open(filename, 'r') as f:
328 ast = parser.parse(f.read())
Rich Lanea06d0c32013-03-25 08:52:03 -0700329 except pyparsing.ParseBaseException as e:
330 print "Parse error in %s: %s" % (os.path.basename(filename), str(e))
331 sys.exit(1)
332
Rich Laned47e5a22013-05-09 14:21:16 -0700333 # Create the OFInput from the AST
334 try:
335 ofinput = frontend.create_ofinput(ast)
336 except frontend.InputError as e:
337 print "Error in %s: %s" % (os.path.basename(filename), str(e))
Rich Lanea06d0c32013-03-25 08:52:03 -0700338 sys.exit(1)
339
340 return ofinput
341
342def order_and_assign_object_ids():
343 """
344 Order all classes and assign object ids to all classes.
345
346 This is done to promote a reasonable order of the objects, putting
347 messages first followed by non-messages. No assumptions should be
348 made about the order, nor about contiguous numbering. However, the
Andreas Wundsam53256162013-05-02 14:05:53 -0700349 numbers should all be reasonably small allowing arrays indexed by
Rich Lanea06d0c32013-03-25 08:52:03 -0700350 these enum values to be defined.
351 """
352
353 # Generate separate message and non-message ordered lists
354 for cls in of_g.unified:
355 if loxi_utils.class_is_message(cls):
356 of_g.ordered_messages.append(cls)
357 elif loxi_utils.class_is_list(cls):
358 of_g.ordered_list_objects.append(cls)
359 else:
360 of_g.ordered_non_messages.append(cls)
361
Rich Lanea06d0c32013-03-25 08:52:03 -0700362 of_g.ordered_messages.sort()
363 of_g.ordered_pseudo_objects.sort()
364 of_g.ordered_non_messages.sort()
365 of_g.ordered_list_objects.sort()
366 of_g.standard_class_order.extend(of_g.ordered_messages)
367 of_g.standard_class_order.extend(of_g.ordered_non_messages)
368 of_g.standard_class_order.extend(of_g.ordered_list_objects)
369
370 # This includes pseudo classes for which most code is not generated
371 of_g.all_class_order.extend(of_g.ordered_messages)
372 of_g.all_class_order.extend(of_g.ordered_non_messages)
373 of_g.all_class_order.extend(of_g.ordered_list_objects)
374 of_g.all_class_order.extend(of_g.ordered_pseudo_objects)
375
376 # Assign object IDs
377 for cls in of_g.ordered_messages:
378 of_g.unified[cls]["object_id"] = of_g.object_id
379 of_g.object_id += 1
380 for cls in of_g.ordered_non_messages:
381 of_g.unified[cls]["object_id"] = of_g.object_id
382 of_g.object_id += 1
383 for cls in of_g.ordered_list_objects:
384 of_g.unified[cls]["object_id"] = of_g.object_id
385 of_g.object_id += 1
386 for cls in of_g.ordered_pseudo_objects:
387 of_g.unified[cls] = {}
388 of_g.unified[cls]["object_id"] = of_g.object_id
389 of_g.object_id += 1
390
Rich Lanea06d0c32013-03-25 08:52:03 -0700391
392def initialize_versions():
393 """
394 Create an empty datastructure for each target version.
395 """
396
397 for wire_version in of_g.target_version_list:
398 version_name = of_g.of_version_wire2name[wire_version]
399 of_g.wire_ver_map[wire_version] = version_name
400 versions[version_name] = dict(
401 version_name = version_name,
402 wire_version = wire_version,
403 classes = {})
404 of_g.ordered_classes[wire_version] = []
405
406
407def read_input():
408 """
409 Read in from files given on command line and update global state
410
411 @fixme Should select versions to support from command line
412 """
413
Rich Lanec9e111d2013-05-09 16:10:30 -0700414 ofinputs_by_version = collections.defaultdict(lambda: [])
Rich Lanea06d0c32013-03-25 08:52:03 -0700415 filenames = sorted(glob.glob("%s/openflow_input/*" % root_dir))
416
Rich Laned23c0132013-05-16 16:18:54 -0700417 # Ignore emacs backup files
418 filenames = [x for x in filenames if not x.endswith('~')]
419
Rich Lanea06d0c32013-03-25 08:52:03 -0700420 for filename in filenames:
421 log("Processing struct file: " + filename)
422 ofinput = process_input_file(filename)
423
424 # Populate global state
425 for wire_version in ofinput.wire_versions:
Rich Lanec9e111d2013-05-09 16:10:30 -0700426 ofinputs_by_version[wire_version].append(ofinput)
Rich Lanea06d0c32013-03-25 08:52:03 -0700427 version_name = of_g.of_version_wire2name[wire_version]
Rich Lanea06d0c32013-03-25 08:52:03 -0700428
Rich Lane4d9f0f62013-05-09 15:50:57 -0700429 for ofclass in ofinput.classes:
430 of_g.ordered_classes[wire_version].append(ofclass.name)
431 legacy_members = []
432 pad_count = 0
433 for m in ofclass.members:
434 if type(m) == OFPadMember:
435 m_name = 'pad%d' % pad_count
436 if m_name == 'pad0': m_name = 'pad'
437 legacy_members.append(dict(m_type='uint8_t[%d]' % m.length,
438 name=m_name))
439 pad_count += 1
440 else:
Rich Lanebe90eae2013-07-22 16:44:26 -0700441 # HACK the C backend does not yet support of_oxm_t
442 if m.oftype == 'of_oxm_t':
443 m_type = 'of_octets_t'
444 else:
Andreas Wundsam37c7bc22013-09-17 13:48:35 -0700445 enum = find(lambda e: e.name == m.oftype, ofinput.enums)
446 if enum and "wire_type" in enum.params:
447 m_type = enum.params["wire_type"]
448 else:
449 m_type = m.oftype
Rich Lanebe90eae2013-07-22 16:44:26 -0700450 legacy_members.append(dict(m_type=m_type, name=m.name))
Rich Lane4d9f0f62013-05-09 15:50:57 -0700451 versions[version_name]['classes'][ofclass.name] = legacy_members
452
453 for enum in ofinput.enums:
Andreas Wundsam4ee51462013-07-30 11:00:37 -0700454 for entry in enum.entries:
Rich Lane38388e62013-04-08 14:09:46 -0700455 identifiers.add_identifier(
Andreas Wundsam4ee51462013-07-30 11:00:37 -0700456 translation.loxi_name(entry.name),
457 entry.name, enum.name, entry.value, wire_version,
Rich Lane38388e62013-04-08 14:09:46 -0700458 of_g.identifiers, of_g.identifiers_by_group)
459
Rich Lanec9e111d2013-05-09 16:10:30 -0700460 for wire_version, ofinputs in ofinputs_by_version.items():
461 ofprotocol = OFProtocol(wire_version=wire_version, classes=[], enums=[])
462 for ofinput in ofinputs:
463 ofprotocol.classes.extend(ofinput.classes)
464 ofprotocol.enums.extend(ofinput.enums)
Rich Laned7977782013-05-09 16:51:40 -0700465 ofprotocol.classes.sort(key=lambda ofclass: ofclass.name)
Rich Lanec9e111d2013-05-09 16:10:30 -0700466 of_g.ir[wire_version] = ofprotocol
467
Rich Lane4db4d042013-05-13 18:13:48 -0700468def populate_type_maps():
469 """
470 Use the type members in the IR to fill out the legacy type_maps.
471 """
472
473 def split_inherited_cls(cls):
474 if cls == 'of_meter_band_stats': # HACK not a subtype of of_meter_band
475 return None, None
476 for parent in sorted(type_maps.inheritance_data.keys(), reverse=True):
477 if cls.startswith(parent):
478 return (parent, cls[len(parent)+1:])
479 return None, None
480
481 def find_experimenter(parent, cls):
482 for experimenter in sorted(of_g.experimenter_name_to_id.keys(), reverse=True):
483 prefix = parent + '_' + experimenter
Rich Lane488b3c52013-06-21 18:11:02 -0700484 if cls.startswith(prefix) and cls != prefix:
Rich Lane4db4d042013-05-13 18:13:48 -0700485 return experimenter
486 return None
487
488 def find_type_value(ofclass, m_name):
489 for m in ofclass.members:
490 if isinstance(m, OFTypeMember) and m.name == m_name:
491 return m.value
492 raise KeyError("ver=%d, cls=%s, m_name=%s" % (wire_version, cls, m_name))
493
494 # Most inheritance classes: actions, instructions, etc
495 for wire_version, protocol in of_g.ir.items():
496 for ofclass in protocol.classes:
497 cls = ofclass.name
498 parent, subcls = split_inherited_cls(cls)
499 if not (parent and subcls):
500 continue
501 if parent == 'of_oxm':
Rich Laneb8fc0b72013-10-24 16:55:25 -0700502 type_len = find_type_value(ofclass, 'type_len')
503 oxm_class = (type_len >> 16) & 0xffff
504 if oxm_class != 0x8000:
505 # Do not include experimenter OXMs in the main table
506 val = type_maps.invalid_type
507 else:
508 val = (type_len >> 8) & 0xff
Rich Lane4db4d042013-05-13 18:13:48 -0700509 else:
510 val = find_type_value(ofclass, 'type')
511 type_maps.inheritance_data[parent][wire_version][subcls] = val
512
513 # Extensions (only actions for now)
514 experimenter = find_experimenter(parent, cls)
515 if parent == 'of_action' and experimenter:
516 val = find_type_value(ofclass, 'subtype')
517 type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
518 if wire_version >= of_g.VERSION_1_3:
519 cls2 = parent + "_id" + cls[len(parent):]
520 type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
521
522 # Messages
523 for wire_version, protocol in of_g.ir.items():
524 for ofclass in protocol.classes:
525 cls = ofclass.name
526 # HACK (though this is what loxi_utils.class_is_message() does)
527 if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
528 continue
Rich Lane488b3c52013-06-21 18:11:02 -0700529 if type_maps.class_is_virtual(cls):
Rich Lane4db4d042013-05-13 18:13:48 -0700530 continue
531 subcls = cls[3:]
532 val = find_type_value(ofclass, 'type')
Rich Lanedf847e32013-05-29 16:57:30 -0700533 if not val in type_maps.message_types[wire_version].values():
534 type_maps.message_types[wire_version][subcls] = val
Rich Lane4db4d042013-05-13 18:13:48 -0700535
536 # Extensions
537 experimenter = find_experimenter('of', cls)
538 if experimenter:
539 val = find_type_value(ofclass, 'subtype')
540 type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
541
542 type_maps.generate_maps()
543
Rich Lanea06d0c32013-03-25 08:52:03 -0700544def analyze_input():
545 """
546 Add information computed from the input, including offsets and
547 lengths of struct members and the set of list and action_id types.
548 """
549
Rich Lane6b435a52013-05-13 15:36:02 -0700550 # Generate header classes for inheritance parents
551 for wire_version, ordered_classes in of_g.ordered_classes.items():
552 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
553 for cls in ordered_classes:
554 if cls in type_maps.inheritance_map:
555 new_cls = cls + '_header'
556 of_g.ordered_classes[wire_version].append(new_cls)
557 classes[new_cls] = classes[cls]
558
Rich Lanea06d0c32013-03-25 08:52:03 -0700559 # Generate action_id classes for OF 1.3
560 for wire_version, ordered_classes in of_g.ordered_classes.items():
561 if not wire_version in [of_g.VERSION_1_3]:
562 continue
563 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
564 for cls in ordered_classes:
565 if not loxi_utils.class_is_action(cls):
566 continue
567 action = cls[10:]
568 if action == '' or action == 'header':
569 continue
570 name = "of_action_id_" + action
571 members = classes["of_action"][:]
572 of_g.ordered_classes[wire_version].append(name)
573 if type_maps.action_id_is_extension(name, wire_version):
574 # Copy the base action classes thru subtype
575 members = classes["of_action_" + action][:4]
576 classes[name] = members
577
578 # @fixme If we support extended actions in OF 1.3, need to add IDs
579 # for them here
580
581 for wire_version in of_g.wire_ver_map.keys():
582 version_name = of_g.of_version_wire2name[wire_version]
583 calculate_offsets_and_lengths(
584 of_g.ordered_classes[wire_version],
585 versions[version_name]['classes'],
586 wire_version)
587
588def unify_input():
589 """
590 Create Unified View of Objects
591 """
592
593 global versions
594
Andreas Wundsam53256162013-05-02 14:05:53 -0700595 # Add classes to unified in wire-format order so that it is easier
Rich Lanea06d0c32013-03-25 08:52:03 -0700596 # to generate things later
597 keys = versions.keys()
598 keys.sort(reverse=True)
599 for version in keys:
600 wire_version = versions[version]["wire_version"]
601 classes = versions[version]["classes"]
602 for cls in of_g.ordered_classes[wire_version]:
603 add_class(wire_version, cls, classes[cls])
604
605
606def log_all_class_info():
607 """
608 Log the results of processing the input
609
610 Debug function
611 """
612
613 for cls in of_g.unified:
614 for v in of_g.unified[cls]:
615 if type(v) == type(0):
616 log("cls: %s. ver: %d. base len %d. %s" %
617 (str(cls), v, of_g.base_length[(cls, v)],
618 loxi_utils.class_is_var_len(cls,v) and "not fixed"
619 or "fixed"))
620 if "use_version" in of_g.unified[cls][v]:
Andreas Wundsam53256162013-05-02 14:05:53 -0700621 log("cls %s: v %d mapped to %d" % (str(cls), v,
Rich Lanea06d0c32013-03-25 08:52:03 -0700622 of_g.unified[cls][v]["use_version"]))
623 if "members" in of_g.unified[cls][v]:
624 for member in of_g.unified[cls][v]["members"]:
625 log(" %-20s: type %-20s. offset %3d" %
626 (member["name"], member["m_type"],
627 member["offset"]))
628
629def generate_all_files():
630 """
631 Create the files for the language target
632 """
Rich Laneda5446f2013-11-10 17:21:48 -0800633 lang_module.generate()
Rich Lanea06d0c32013-03-25 08:52:03 -0700634
635if __name__ == '__main__':
636 of_g.loxigen_log_file = open("loxigen.log", "w")
637 of_g.loxigen_dbg_file = sys.stdout
638
639 of_g.process_commandline()
640 # @fixme Use command line params to select log
641
642 if not config_sanity_check():
643 debug("Config sanity check failed\n")
644 sys.exit(1)
645
646 # Import the language file
647 lang_file = "lang_%s" % of_g.options.lang
648 lang_module = __import__(lang_file)
649
650 # If list files, just list auto-gen files to stdout and exit
651 if of_g.options.list_files:
652 for name in lang_module.targets:
653 print of_g.options.install_dir + '/' + name
654 sys.exit(0)
655
656 log("\nGenerating files for target language %s\n" % of_g.options.lang)
657
Rich Lanea06d0c32013-03-25 08:52:03 -0700658 initialize_versions()
659 read_input()
Rich Lane4db4d042013-05-13 18:13:48 -0700660 populate_type_maps()
Rich Lanea06d0c32013-03-25 08:52:03 -0700661 analyze_input()
662 unify_input()
663 order_and_assign_object_ids()
664 log_all_class_info()
665 generate_all_files()