blob: dcd547e300719b5a4ea5343439ccb2bc65dbd117 [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:
445 m_type = m.oftype
446 legacy_members.append(dict(m_type=m_type, name=m.name))
Rich Lane4d9f0f62013-05-09 15:50:57 -0700447 versions[version_name]['classes'][ofclass.name] = legacy_members
448
449 for enum in ofinput.enums:
Andreas Wundsam4ee51462013-07-30 11:00:37 -0700450 for entry in enum.entries:
Rich Lane38388e62013-04-08 14:09:46 -0700451 identifiers.add_identifier(
Andreas Wundsam4ee51462013-07-30 11:00:37 -0700452 translation.loxi_name(entry.name),
453 entry.name, enum.name, entry.value, wire_version,
Rich Lane38388e62013-04-08 14:09:46 -0700454 of_g.identifiers, of_g.identifiers_by_group)
455
Rich Lanec9e111d2013-05-09 16:10:30 -0700456 for wire_version, ofinputs in ofinputs_by_version.items():
457 ofprotocol = OFProtocol(wire_version=wire_version, classes=[], enums=[])
458 for ofinput in ofinputs:
459 ofprotocol.classes.extend(ofinput.classes)
460 ofprotocol.enums.extend(ofinput.enums)
Rich Laned7977782013-05-09 16:51:40 -0700461 ofprotocol.classes.sort(key=lambda ofclass: ofclass.name)
Rich Lanec9e111d2013-05-09 16:10:30 -0700462 of_g.ir[wire_version] = ofprotocol
463
Rich Lane4db4d042013-05-13 18:13:48 -0700464def populate_type_maps():
465 """
466 Use the type members in the IR to fill out the legacy type_maps.
467 """
468
469 def split_inherited_cls(cls):
470 if cls == 'of_meter_band_stats': # HACK not a subtype of of_meter_band
471 return None, None
472 for parent in sorted(type_maps.inheritance_data.keys(), reverse=True):
473 if cls.startswith(parent):
474 return (parent, cls[len(parent)+1:])
475 return None, None
476
477 def find_experimenter(parent, cls):
478 for experimenter in sorted(of_g.experimenter_name_to_id.keys(), reverse=True):
479 prefix = parent + '_' + experimenter
Rich Lane488b3c52013-06-21 18:11:02 -0700480 if cls.startswith(prefix) and cls != prefix:
Rich Lane4db4d042013-05-13 18:13:48 -0700481 return experimenter
482 return None
483
484 def find_type_value(ofclass, m_name):
485 for m in ofclass.members:
486 if isinstance(m, OFTypeMember) and m.name == m_name:
487 return m.value
488 raise KeyError("ver=%d, cls=%s, m_name=%s" % (wire_version, cls, m_name))
489
490 # Most inheritance classes: actions, instructions, etc
491 for wire_version, protocol in of_g.ir.items():
492 for ofclass in protocol.classes:
493 cls = ofclass.name
494 parent, subcls = split_inherited_cls(cls)
495 if not (parent and subcls):
496 continue
497 if parent == 'of_oxm':
498 val = (find_type_value(ofclass, 'type_len') >> 8) & 0xff
499 else:
500 val = find_type_value(ofclass, 'type')
501 type_maps.inheritance_data[parent][wire_version][subcls] = val
502
503 # Extensions (only actions for now)
504 experimenter = find_experimenter(parent, cls)
505 if parent == 'of_action' and experimenter:
506 val = find_type_value(ofclass, 'subtype')
507 type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
508 if wire_version >= of_g.VERSION_1_3:
509 cls2 = parent + "_id" + cls[len(parent):]
510 type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
511
512 # Messages
513 for wire_version, protocol in of_g.ir.items():
514 for ofclass in protocol.classes:
515 cls = ofclass.name
516 # HACK (though this is what loxi_utils.class_is_message() does)
517 if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
518 continue
Rich Lane488b3c52013-06-21 18:11:02 -0700519 if type_maps.class_is_virtual(cls):
Rich Lane4db4d042013-05-13 18:13:48 -0700520 continue
521 subcls = cls[3:]
522 val = find_type_value(ofclass, 'type')
Rich Lanedf847e32013-05-29 16:57:30 -0700523 if not val in type_maps.message_types[wire_version].values():
524 type_maps.message_types[wire_version][subcls] = val
Rich Lane4db4d042013-05-13 18:13:48 -0700525
526 # Extensions
527 experimenter = find_experimenter('of', cls)
528 if experimenter:
529 val = find_type_value(ofclass, 'subtype')
530 type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
531
532 type_maps.generate_maps()
533
Rich Lanea06d0c32013-03-25 08:52:03 -0700534def analyze_input():
535 """
536 Add information computed from the input, including offsets and
537 lengths of struct members and the set of list and action_id types.
538 """
539
Rich Lane6b435a52013-05-13 15:36:02 -0700540 # Generate header classes for inheritance parents
541 for wire_version, ordered_classes in of_g.ordered_classes.items():
542 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
543 for cls in ordered_classes:
544 if cls in type_maps.inheritance_map:
545 new_cls = cls + '_header'
546 of_g.ordered_classes[wire_version].append(new_cls)
547 classes[new_cls] = classes[cls]
548
Rich Lanea06d0c32013-03-25 08:52:03 -0700549 # Generate action_id classes for OF 1.3
550 for wire_version, ordered_classes in of_g.ordered_classes.items():
551 if not wire_version in [of_g.VERSION_1_3]:
552 continue
553 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
554 for cls in ordered_classes:
555 if not loxi_utils.class_is_action(cls):
556 continue
557 action = cls[10:]
558 if action == '' or action == 'header':
559 continue
560 name = "of_action_id_" + action
561 members = classes["of_action"][:]
562 of_g.ordered_classes[wire_version].append(name)
563 if type_maps.action_id_is_extension(name, wire_version):
564 # Copy the base action classes thru subtype
565 members = classes["of_action_" + action][:4]
566 classes[name] = members
567
568 # @fixme If we support extended actions in OF 1.3, need to add IDs
569 # for them here
570
571 for wire_version in of_g.wire_ver_map.keys():
572 version_name = of_g.of_version_wire2name[wire_version]
573 calculate_offsets_and_lengths(
574 of_g.ordered_classes[wire_version],
575 versions[version_name]['classes'],
576 wire_version)
577
578def unify_input():
579 """
580 Create Unified View of Objects
581 """
582
583 global versions
584
Andreas Wundsam53256162013-05-02 14:05:53 -0700585 # Add classes to unified in wire-format order so that it is easier
Rich Lanea06d0c32013-03-25 08:52:03 -0700586 # to generate things later
587 keys = versions.keys()
588 keys.sort(reverse=True)
589 for version in keys:
590 wire_version = versions[version]["wire_version"]
591 classes = versions[version]["classes"]
592 for cls in of_g.ordered_classes[wire_version]:
593 add_class(wire_version, cls, classes[cls])
594
595
596def log_all_class_info():
597 """
598 Log the results of processing the input
599
600 Debug function
601 """
602
603 for cls in of_g.unified:
604 for v in of_g.unified[cls]:
605 if type(v) == type(0):
606 log("cls: %s. ver: %d. base len %d. %s" %
607 (str(cls), v, of_g.base_length[(cls, v)],
608 loxi_utils.class_is_var_len(cls,v) and "not fixed"
609 or "fixed"))
610 if "use_version" in of_g.unified[cls][v]:
Andreas Wundsam53256162013-05-02 14:05:53 -0700611 log("cls %s: v %d mapped to %d" % (str(cls), v,
Rich Lanea06d0c32013-03-25 08:52:03 -0700612 of_g.unified[cls][v]["use_version"]))
613 if "members" in of_g.unified[cls][v]:
614 for member in of_g.unified[cls][v]["members"]:
615 log(" %-20s: type %-20s. offset %3d" %
616 (member["name"], member["m_type"],
617 member["offset"]))
618
619def generate_all_files():
620 """
621 Create the files for the language target
622 """
623 for (name, fn) in lang_module.targets.items():
624 path = of_g.options.install_dir + '/' + name
625 os.system("mkdir -p %s" % os.path.dirname(path))
626 with open(path, "w") as outfile:
627 fn(outfile, os.path.basename(name))
628 print("Wrote contents for " + name)
629
630if __name__ == '__main__':
631 of_g.loxigen_log_file = open("loxigen.log", "w")
632 of_g.loxigen_dbg_file = sys.stdout
633
634 of_g.process_commandline()
635 # @fixme Use command line params to select log
636
637 if not config_sanity_check():
638 debug("Config sanity check failed\n")
639 sys.exit(1)
640
641 # Import the language file
642 lang_file = "lang_%s" % of_g.options.lang
643 lang_module = __import__(lang_file)
644
645 # If list files, just list auto-gen files to stdout and exit
646 if of_g.options.list_files:
647 for name in lang_module.targets:
648 print of_g.options.install_dir + '/' + name
649 sys.exit(0)
650
651 log("\nGenerating files for target language %s\n" % of_g.options.lang)
652
Rich Lanea06d0c32013-03-25 08:52:03 -0700653 initialize_versions()
654 read_input()
Rich Lane4db4d042013-05-13 18:13:48 -0700655 populate_type_maps()
Rich Lanea06d0c32013-03-25 08:52:03 -0700656 analyze_input()
657 unify_input()
658 order_and_assign_object_ids()
659 log_all_class_info()
660 generate_all_files()