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