blob: ace386d122a072fb2536588b7c40a99215c688d6 [file] [log] [blame]
Andreas Wundsam76db0062013-11-15 13:34:41 -08001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
28import sys
29
30import re
31import string
32import os
33import glob
34import copy
35import collections
36import c_gen.of_g_legacy as of_g
37import c_gen.type_maps as type_maps
38import c_gen.loxi_utils_legacy as loxi_utils
39import loxi_globals
40import c_gen.identifiers as identifiers
41import pyparsing
42import loxi_front_end.parser as parser
43import c_gen.translation as translation
44import loxi_front_end.frontend as frontend
45from loxi_ir import *
46from generic_utils import *
47
48root_dir = os.path.dirname(os.path.realpath(__file__))
49
50versions = {}
51# TODO: Put these in a class so they get documented
52
53## Dict indexed by version giving all info related to version
54#
55# This is local; after processing, the information is stored in
56# of_g variables.
57
58def add_class(wire_version, cls, members):
59 """
60 Process a class for the given version and update the unified
61 list of classes as needed.
62
63 @param wire_version The wire version for this class defn
64 @param cls The name of the class being added
65 @param members The list of members with offsets calculated
66 """
67 memid = 0
68
69 sig = loxi_utils.class_signature(members)
70 if cls in of_g.unified:
71 uc = of_g.unified[cls]
72 if wire_version in uc:
73 debug("Error adding %s to unified. Wire ver %d exists" %
74 (cls, wire_version))
75 sys.exit(1)
76 uc[wire_version] = {}
77 # Check for a matching signature
78 for wver in uc:
79 if type(wver) != type(0): continue
80 if wver == wire_version: continue
81 if not "use_version" in uc[wver]:
82 if sig == loxi_utils.class_signature(uc[wver]["members"]):
83 log("Matched %s, ver %d to ver %d" %
84 (cls, wire_version, wver))
85 # have a match with existing version
86 uc[wire_version]["use_version"] = wver
87 # What else to do?
88 return
89 else: # Haven't seen this entry before
90 log("Adding %s to unified list, ver %d" % (cls, wire_version))
91 of_g.unified[cls] = dict(union={})
92 uc = of_g.unified[cls]
93
94 # At this point, need to add members for this version
95 uc[wire_version] = dict(members = members)
96
97 # Per member processing:
98 # Add to union list (I'm sure there's a better way)
99 # Check if it's a list
100 union = uc["union"]
101 if not cls in of_g.ordered_members:
102 of_g.ordered_members[cls] = []
103 for member in members:
104 m_name = member["name"]
105 m_type = member["m_type"]
106 if m_name.find("pad") == 0:
107 continue
108 if m_name in union:
109 if not m_type == union[m_name]["m_type"]:
110 debug("ERROR: CLASS: %s. VERSION %d. MEMBER: %s. TYPE: %s" %
111 (cls, wire_version, m_name, m_type))
112 debug(" Type conflict adding member to unified set.")
113 debug(" Current union[%s]:" % m_name)
114 debug(union[m_name])
115 sys.exit(1)
116 else:
117 union[m_name] = dict(m_type=m_type, memid=memid)
118 memid += 1
119 if not m_name in of_g.ordered_members[cls]:
120 of_g.ordered_members[cls].append(m_name)
121
Andreas Wundsam76db0062013-11-15 13:34:41 -0800122def order_and_assign_object_ids():
123 """
124 Order all classes and assign object ids to all classes.
125
126 This is done to promote a reasonable order of the objects, putting
127 messages first followed by non-messages. No assumptions should be
128 made about the order, nor about contiguous numbering. However, the
129 numbers should all be reasonably small allowing arrays indexed by
130 these enum values to be defined.
131 """
132
133 # Generate separate message and non-message ordered lists
134 for cls in of_g.unified:
135 if loxi_utils.class_is_message(cls):
136 of_g.ordered_messages.append(cls)
137 elif loxi_utils.class_is_list(cls):
138 of_g.ordered_list_objects.append(cls)
139 else:
140 of_g.ordered_non_messages.append(cls)
141
142 of_g.ordered_messages.sort()
143 of_g.ordered_pseudo_objects.sort()
144 of_g.ordered_non_messages.sort()
145 of_g.ordered_list_objects.sort()
146 of_g.standard_class_order.extend(of_g.ordered_messages)
147 of_g.standard_class_order.extend(of_g.ordered_non_messages)
148 of_g.standard_class_order.extend(of_g.ordered_list_objects)
149
150 # This includes pseudo classes for which most code is not generated
151 of_g.all_class_order.extend(of_g.ordered_messages)
152 of_g.all_class_order.extend(of_g.ordered_non_messages)
153 of_g.all_class_order.extend(of_g.ordered_list_objects)
154 of_g.all_class_order.extend(of_g.ordered_pseudo_objects)
155
156 # Assign object IDs
157 for cls in of_g.ordered_messages:
158 of_g.unified[cls]["object_id"] = of_g.object_id
159 of_g.object_id += 1
160 for cls in of_g.ordered_non_messages:
161 of_g.unified[cls]["object_id"] = of_g.object_id
162 of_g.object_id += 1
163 for cls in of_g.ordered_list_objects:
164 of_g.unified[cls]["object_id"] = of_g.object_id
165 of_g.object_id += 1
Andreas Wundsam76db0062013-11-15 13:34:41 -0800166
167
168def initialize_versions():
169 """
170 Create an empty datastructure for each target version.
171 """
172
173 for version in loxi_globals.OFVersions.target_versions:
174 wire_version = version.wire_version
175 version_name = of_g.of_version_wire2name[wire_version]
176 of_g.wire_ver_map[wire_version] = version_name
177 versions[version_name] = dict(
178 version_name = version_name,
179 wire_version = wire_version,
180 classes = {})
181 of_g.ordered_classes[wire_version] = []
182
183 of_g.target_version_list = [ v.wire_version for v in loxi_globals.OFVersions.target_versions ]
184
185def build_ordered_classes():
186 """
187 Read in from files given on command line and update global state
188
189 @fixme Should select versions to support from command line
190 """
191
192 for version, protocol in loxi_globals.ir.items():
193 wire_version = version.wire_version
194 # Populate global state
195 version_name = of_g.of_version_wire2name[wire_version]
196
197 for ofclass in protocol.classes:
Andreas Wundsam76db0062013-11-15 13:34:41 -0800198 of_g.ordered_classes[wire_version].append(ofclass.name)
199 legacy_members = []
200 pad_count = 0
201 for m in ofclass.members:
202 if type(m) == OFPadMember:
Rich Lane2fe8e912014-10-16 14:28:26 -0700203 continue
Andreas Wundsam76db0062013-11-15 13:34:41 -0800204 else:
Rich Lanec247ade2014-11-07 09:33:24 -0800205 if m.oftype.find("list(") == 0:
Rich Lane2fe8e912014-10-16 14:28:26 -0700206 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
207 m_type = list_name + "_t"
Andreas Wundsam76db0062013-11-15 13:34:41 -0800208 else:
209 enum = find(lambda e: e.name == m.oftype, protocol.enums)
210 if enum and "wire_type" in enum.params:
211 m_type = enum.params["wire_type"]
212 else:
213 m_type = m.oftype
Rich Lane2fe8e912014-10-16 14:28:26 -0700214
215 if m.offset is None:
216 m_offset = -1
217 else:
218 m_offset = m.offset
219
220 legacy_members.append(dict(m_type=m_type, name=m.name, offset=m_offset))
Andreas Wundsam76db0062013-11-15 13:34:41 -0800221 versions[version_name]['classes'][ofclass.name] = legacy_members
222
Rich Lane2fe8e912014-10-16 14:28:26 -0700223 of_g.base_length[(ofclass.name, version.wire_version)] = ofclass.base_length
224 if ofclass.is_fixed_length:
225 of_g.is_fixed_length.add((ofclass.name, version.wire_version))
226
Andreas Wundsam76db0062013-11-15 13:34:41 -0800227 for enum in protocol.enums:
228 for entry in enum.entries:
229 identifiers.add_identifier(
230 translation.loxi_name(entry.name),
231 entry.name, enum.name, entry.value, wire_version,
232 of_g.identifiers, of_g.identifiers_by_group)
233
234def populate_type_maps():
235 """
236 Use the type members in the IR to fill out the legacy type_maps.
237 """
Andreas Wundsam76db0062013-11-15 13:34:41 -0800238 type_maps.generate_maps()
239
240def analyze_input():
241 """
242 Add information computed from the input, including offsets and
243 lengths of struct members and the set of list and action_id types.
244 """
Rich Lane2fe8e912014-10-16 14:28:26 -0700245 # Create lists
246 for version, protocol in loxi_globals.ir.items():
247 lists = set()
248 classes = versions[of_g.of_version_wire2name[version.wire_version]]['classes']
249
250 for ofclass in protocol.classes:
251 for m in ofclass.members:
252 if isinstance(m, OFDataMember) and m.oftype.find("list(") == 0:
253 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
254 lists.add(list_name)
255
256 for list_type in lists:
257 classes[list_type] = []
258 of_g.ordered_classes[version.wire_version].append(list_type)
259 of_g.base_length[(list_type, version.wire_version)] = 0
260
261 # Find special offsets
262 # These are defined as members (except padding) that don't have a fixed
263 # offset. The special_offsets map stores the name of the previous member.
264 for version, protocol in loxi_globals.ir.items():
265 for ofclass in protocol.classes:
266 prev_member = None
267 for m in ofclass.members:
268 if isinstance(m, OFPadMember):
269 continue
270 if m.offset == None:
271 old = of_g.special_offsets.get((ofclass.name, m.name))
272 if old and old != prev_member.name:
273 raise Exception("Error: special offset changed: version=%s cls=%s member=%s old=%s new=%s" %
274 (version, ofclass.name, m.name, old, prev_member.name))
275 of_g.special_offsets[(ofclass.name, m.name)] = prev_member.name
276 prev_member = m
Andreas Wundsam76db0062013-11-15 13:34:41 -0800277
278def unify_input():
279 """
280 Create Unified View of Objects
281 """
282
283 global versions
284
285 # Add classes to unified in wire-format order so that it is easier
286 # to generate things later
287 keys = versions.keys()
288 keys.sort(reverse=True)
289 for version in keys:
290 wire_version = versions[version]["wire_version"]
291 classes = versions[version]["classes"]
292 for cls in of_g.ordered_classes[wire_version]:
293 add_class(wire_version, cls, classes[cls])