blob: 36d6872f242f999abb91353e72fcdaa06cde8c7b [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
362 of_g.ordered_pseudo_objects.append("of_stats_request")
363 of_g.ordered_pseudo_objects.append("of_stats_reply")
364 of_g.ordered_pseudo_objects.append("of_flow_mod")
365
366 of_g.ordered_messages.sort()
367 of_g.ordered_pseudo_objects.sort()
368 of_g.ordered_non_messages.sort()
369 of_g.ordered_list_objects.sort()
370 of_g.standard_class_order.extend(of_g.ordered_messages)
371 of_g.standard_class_order.extend(of_g.ordered_non_messages)
372 of_g.standard_class_order.extend(of_g.ordered_list_objects)
373
374 # This includes pseudo classes for which most code is not generated
375 of_g.all_class_order.extend(of_g.ordered_messages)
376 of_g.all_class_order.extend(of_g.ordered_non_messages)
377 of_g.all_class_order.extend(of_g.ordered_list_objects)
378 of_g.all_class_order.extend(of_g.ordered_pseudo_objects)
379
380 # Assign object IDs
381 for cls in of_g.ordered_messages:
382 of_g.unified[cls]["object_id"] = of_g.object_id
383 of_g.object_id += 1
384 for cls in of_g.ordered_non_messages:
385 of_g.unified[cls]["object_id"] = of_g.object_id
386 of_g.object_id += 1
387 for cls in of_g.ordered_list_objects:
388 of_g.unified[cls]["object_id"] = of_g.object_id
389 of_g.object_id += 1
390 for cls in of_g.ordered_pseudo_objects:
391 of_g.unified[cls] = {}
392 of_g.unified[cls]["object_id"] = of_g.object_id
393 of_g.object_id += 1
394
Rich Lanea06d0c32013-03-25 08:52:03 -0700395
396def initialize_versions():
397 """
398 Create an empty datastructure for each target version.
399 """
400
401 for wire_version in of_g.target_version_list:
402 version_name = of_g.of_version_wire2name[wire_version]
403 of_g.wire_ver_map[wire_version] = version_name
404 versions[version_name] = dict(
405 version_name = version_name,
406 wire_version = wire_version,
407 classes = {})
408 of_g.ordered_classes[wire_version] = []
409
410
411def read_input():
412 """
413 Read in from files given on command line and update global state
414
415 @fixme Should select versions to support from command line
416 """
417
Rich Lanec9e111d2013-05-09 16:10:30 -0700418 ofinputs_by_version = collections.defaultdict(lambda: [])
Rich Lanea06d0c32013-03-25 08:52:03 -0700419 filenames = sorted(glob.glob("%s/openflow_input/*" % root_dir))
420
Rich Laned23c0132013-05-16 16:18:54 -0700421 # Ignore emacs backup files
422 filenames = [x for x in filenames if not x.endswith('~')]
423
Rich Lanea06d0c32013-03-25 08:52:03 -0700424 for filename in filenames:
425 log("Processing struct file: " + filename)
426 ofinput = process_input_file(filename)
427
428 # Populate global state
429 for wire_version in ofinput.wire_versions:
Rich Lanec9e111d2013-05-09 16:10:30 -0700430 ofinputs_by_version[wire_version].append(ofinput)
Rich Lanea06d0c32013-03-25 08:52:03 -0700431 version_name = of_g.of_version_wire2name[wire_version]
Rich Lanea06d0c32013-03-25 08:52:03 -0700432
Rich Lane4d9f0f62013-05-09 15:50:57 -0700433 for ofclass in ofinput.classes:
434 of_g.ordered_classes[wire_version].append(ofclass.name)
435 legacy_members = []
436 pad_count = 0
437 for m in ofclass.members:
438 if type(m) == OFPadMember:
439 m_name = 'pad%d' % pad_count
440 if m_name == 'pad0': m_name = 'pad'
441 legacy_members.append(dict(m_type='uint8_t[%d]' % m.length,
442 name=m_name))
443 pad_count += 1
444 else:
Rich Lanebe90eae2013-07-22 16:44:26 -0700445 # HACK the C backend does not yet support of_oxm_t
446 if m.oftype == 'of_oxm_t':
447 m_type = 'of_octets_t'
448 else:
449 m_type = m.oftype
450 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:
454 for name, value in enum.values:
Rich Lane38388e62013-04-08 14:09:46 -0700455 identifiers.add_identifier(
Rich Lane4d9f0f62013-05-09 15:50:57 -0700456 translation.loxi_name(name),
457 name, enum.name, 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
484 if cls.startswith(prefix):
485 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':
502 val = (find_type_value(ofclass, 'type_len') >> 8) & 0xff
503 else:
504 val = find_type_value(ofclass, 'type')
505 type_maps.inheritance_data[parent][wire_version][subcls] = val
506
507 # Extensions (only actions for now)
508 experimenter = find_experimenter(parent, cls)
509 if parent == 'of_action' and experimenter:
510 val = find_type_value(ofclass, 'subtype')
511 type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
512 if wire_version >= of_g.VERSION_1_3:
513 cls2 = parent + "_id" + cls[len(parent):]
514 type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
515
516 # Messages
517 for wire_version, protocol in of_g.ir.items():
518 for ofclass in protocol.classes:
519 cls = ofclass.name
520 # HACK (though this is what loxi_utils.class_is_message() does)
521 if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
522 continue
523 if cls == 'of_header':
524 continue
525 subcls = cls[3:]
526 val = find_type_value(ofclass, 'type')
Rich Lanedf847e32013-05-29 16:57:30 -0700527 if not val in type_maps.message_types[wire_version].values():
528 type_maps.message_types[wire_version][subcls] = val
Rich Lane4db4d042013-05-13 18:13:48 -0700529
530 # Extensions
531 experimenter = find_experimenter('of', cls)
532 if experimenter:
533 val = find_type_value(ofclass, 'subtype')
534 type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
535
536 type_maps.generate_maps()
537
Rich Lanea06d0c32013-03-25 08:52:03 -0700538def analyze_input():
539 """
540 Add information computed from the input, including offsets and
541 lengths of struct members and the set of list and action_id types.
542 """
543
Rich Lane6b435a52013-05-13 15:36:02 -0700544 # Generate header classes for inheritance parents
545 for wire_version, ordered_classes in of_g.ordered_classes.items():
546 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
547 for cls in ordered_classes:
548 if cls in type_maps.inheritance_map:
549 new_cls = cls + '_header'
550 of_g.ordered_classes[wire_version].append(new_cls)
551 classes[new_cls] = classes[cls]
552
Rich Lanea06d0c32013-03-25 08:52:03 -0700553 # Generate action_id classes for OF 1.3
554 for wire_version, ordered_classes in of_g.ordered_classes.items():
555 if not wire_version in [of_g.VERSION_1_3]:
556 continue
557 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
558 for cls in ordered_classes:
559 if not loxi_utils.class_is_action(cls):
560 continue
561 action = cls[10:]
562 if action == '' or action == 'header':
563 continue
564 name = "of_action_id_" + action
565 members = classes["of_action"][:]
566 of_g.ordered_classes[wire_version].append(name)
567 if type_maps.action_id_is_extension(name, wire_version):
568 # Copy the base action classes thru subtype
569 members = classes["of_action_" + action][:4]
570 classes[name] = members
571
572 # @fixme If we support extended actions in OF 1.3, need to add IDs
573 # for them here
574
575 for wire_version in of_g.wire_ver_map.keys():
576 version_name = of_g.of_version_wire2name[wire_version]
577 calculate_offsets_and_lengths(
578 of_g.ordered_classes[wire_version],
579 versions[version_name]['classes'],
580 wire_version)
581
582def unify_input():
583 """
584 Create Unified View of Objects
585 """
586
587 global versions
588
Andreas Wundsam53256162013-05-02 14:05:53 -0700589 # Add classes to unified in wire-format order so that it is easier
Rich Lanea06d0c32013-03-25 08:52:03 -0700590 # to generate things later
591 keys = versions.keys()
592 keys.sort(reverse=True)
593 for version in keys:
594 wire_version = versions[version]["wire_version"]
595 classes = versions[version]["classes"]
596 for cls in of_g.ordered_classes[wire_version]:
597 add_class(wire_version, cls, classes[cls])
598
599
600def log_all_class_info():
601 """
602 Log the results of processing the input
603
604 Debug function
605 """
606
607 for cls in of_g.unified:
608 for v in of_g.unified[cls]:
609 if type(v) == type(0):
610 log("cls: %s. ver: %d. base len %d. %s" %
611 (str(cls), v, of_g.base_length[(cls, v)],
612 loxi_utils.class_is_var_len(cls,v) and "not fixed"
613 or "fixed"))
614 if "use_version" in of_g.unified[cls][v]:
Andreas Wundsam53256162013-05-02 14:05:53 -0700615 log("cls %s: v %d mapped to %d" % (str(cls), v,
Rich Lanea06d0c32013-03-25 08:52:03 -0700616 of_g.unified[cls][v]["use_version"]))
617 if "members" in of_g.unified[cls][v]:
618 for member in of_g.unified[cls][v]["members"]:
619 log(" %-20s: type %-20s. offset %3d" %
620 (member["name"], member["m_type"],
621 member["offset"]))
622
623def generate_all_files():
624 """
625 Create the files for the language target
626 """
627 for (name, fn) in lang_module.targets.items():
628 path = of_g.options.install_dir + '/' + name
629 os.system("mkdir -p %s" % os.path.dirname(path))
630 with open(path, "w") as outfile:
631 fn(outfile, os.path.basename(name))
632 print("Wrote contents for " + name)
633
634if __name__ == '__main__':
635 of_g.loxigen_log_file = open("loxigen.log", "w")
636 of_g.loxigen_dbg_file = sys.stdout
637
638 of_g.process_commandline()
639 # @fixme Use command line params to select log
640
641 if not config_sanity_check():
642 debug("Config sanity check failed\n")
643 sys.exit(1)
644
645 # Import the language file
646 lang_file = "lang_%s" % of_g.options.lang
647 lang_module = __import__(lang_file)
648
649 # If list files, just list auto-gen files to stdout and exit
650 if of_g.options.list_files:
651 for name in lang_module.targets:
652 print of_g.options.install_dir + '/' + name
653 sys.exit(0)
654
655 log("\nGenerating files for target language %s\n" % of_g.options.lang)
656
Rich Lanea06d0c32013-03-25 08:52:03 -0700657 initialize_versions()
658 read_input()
Rich Lane4db4d042013-05-13 18:13:48 -0700659 populate_type_maps()
Rich Lanea06d0c32013-03-25 08:52:03 -0700660 analyze_input()
661 unify_input()
662 order_and_assign_object_ids()
663 log_all_class_info()
664 generate_all_files()