blob: 6f0f6e98efb9fab4440aa357c0e417fa1630a32d [file] [log] [blame]
Rich Lanebdd8e292013-12-06 17:37:39 -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
28"""
29Code generation
30
31These functions extract data from the IR and render templates with it.
32"""
33
34from collections import namedtuple
35from itertools import groupby
Rich Lanedef2e512013-12-15 15:54:02 -080036from StringIO import StringIO
Rich Lanebdd8e292013-12-06 17:37:39 -080037import template_utils
Rich Lanef51062b2014-02-24 23:08:26 -080038from generic_utils import chunks
Rich Lanebdd8e292013-12-06 17:37:39 -080039import loxi_globals
40import loxi_ir.ir as ir
41import util
Rich Lanece2e4642013-12-15 12:05:45 -080042import c_code_gen
Rich Lane8c4c23f2013-12-15 13:22:13 -080043import c_gen.of_g_legacy as of_g
Rich Lanecce961d2013-12-15 14:20:42 -080044import c_gen.type_maps as type_maps
Rich Lanec0e20ff2013-12-15 23:40:31 -080045import c_gen.c_type_maps as c_type_maps
Rich Lanebdd8e292013-12-06 17:37:39 -080046
Rich Lanef51062b2014-02-24 23:08:26 -080047CLASS_CHUNK_SIZE = 32
48
Rich Lane22811f52013-12-15 15:28:03 -080049PushWireTypesData = namedtuple('PushWireTypesData',
Rich Lanebdd8e292013-12-06 17:37:39 -080050 ['class_name', 'versioned_type_members'])
51PushWireTypesMember = namedtuple('PushWireTypesMember',
52 ['name', 'offset', 'length', 'value'])
53
Rich Lane22811f52013-12-15 15:28:03 -080054def push_wire_types_data(uclass):
55 if uclass.virtual or not uclass.has_type_members:
56 return None
Rich Lanebdd8e292013-12-06 17:37:39 -080057
Rich Lane22811f52013-12-15 15:28:03 -080058 # Generate a dict of version -> list of PushWireTypesMember
59 type_members_by_version = {}
60 for version, ofclass in sorted(uclass.version_classes.items()):
61 pwtms = []
62 for m in ofclass.members:
63 if isinstance(m, ir.OFTypeMember):
64 if m.name == "version" and m.value == version.wire_version:
65 # Special case for version
66 pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, "obj->version"))
67 else:
Rich Laned0c58712013-12-18 10:02:47 -080068 pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, hex(m.value)))
Rich Lane22811f52013-12-15 15:28:03 -080069 type_members_by_version[version] = pwtms
Rich Lanebdd8e292013-12-06 17:37:39 -080070
Rich Lane22811f52013-12-15 15:28:03 -080071 # Merge versions with identical type members
72 all_versions = sorted(type_members_by_version.keys())
73 versioned_type_members = []
74 for pwtms, versions in groupby(all_versions, type_members_by_version.get):
75 versioned_type_members.append((pwtms, list(versions)))
Rich Lanebdd8e292013-12-06 17:37:39 -080076
Rich Lane22811f52013-12-15 15:28:03 -080077 return PushWireTypesData(
78 class_name=uclass.name,
79 versioned_type_members=versioned_type_members)
Rich Lanece2e4642013-12-15 12:05:45 -080080
Rich Laneb9fab4b2014-03-04 18:44:08 -080081ParseWireTypesData = namedtuple('ParseWireTypesData',
82 ['class_name', 'versioned'])
83ParseWireTypesVersion = namedtuple('ParseWireTypesVersion',
84 ['discriminator', 'subclasses'])
85ParseWireTypesSubclass = namedtuple('ParseWireTypesSubclass',
86 ['class_name', 'value', 'virtual'])
87
88def parse_wire_types_data(uclass):
89 if not uclass.virtual:
90 return None
91
92 discriminator = uclass.discriminator
93
94 # Generate a dict of version -> ParseWireTypesVersion
95 versioned = {}
96 for version, ofclass in sorted(uclass.version_classes.items()):
97 subclasses = [ParseWireTypesSubclass(class_name=subclass.name,
98 value=subclass.member_by_name(discriminator.name).value,
99 virtual=subclass.virtual)
100 for subclass in ofclass.protocol.classes if subclass.superclass and subclass.superclass.name == ofclass.name]
101
102 subclasses.sort(key=lambda x: x.value)
103 versioned[version] = ParseWireTypesVersion(discriminator=discriminator,
104 subclasses=subclasses)
105
106 return ParseWireTypesData(class_name=uclass.name,
107 versioned=sorted(versioned.items()))
108
Rich Lanef51062b2014-02-24 23:08:26 -0800109# Output multiple LOCI classes into each C file. This reduces the overhead of
110# parsing header files, which takes longer than compiling the actual code
111# for many classes. It also reduces the compiled code size.
Rich Lanece2e4642013-12-15 12:05:45 -0800112def generate_classes(install_dir):
Rich Lanef51062b2014-02-24 23:08:26 -0800113 for i, chunk in enumerate(chunks(loxi_globals.unified.classes, CLASS_CHUNK_SIZE)):
114 with template_utils.open_output(install_dir, "loci/src/class%02d.c" % i) as out:
115 for uclass in chunk:
116 util.render_template(out, "class.c",
Rich Laneb9fab4b2014-03-04 18:44:08 -0800117 push_wire_types_data=push_wire_types_data(uclass),
118 parse_wire_types_data=parse_wire_types_data(uclass))
Rich Lanef51062b2014-02-24 23:08:26 -0800119 # Append legacy generated code
120 c_code_gen.gen_new_function_definitions(out, uclass.name)
121 c_code_gen.gen_accessor_definitions(out, uclass.name)
Rich Lane8c4c23f2013-12-15 13:22:13 -0800122
Rich Lane573d2b22013-12-15 13:31:27 -0800123# TODO remove header classes and use the corresponding class instead
124def generate_header_classes(install_dir):
125 for cls in of_g.standard_class_order:
126 if cls.find("_header") < 0:
127 continue
128 with template_utils.open_output(install_dir, "loci/src/%s.c" % cls) as out:
Rich Lane22811f52013-12-15 15:28:03 -0800129 util.render_template(out, "class.c",
Rich Laneb9fab4b2014-03-04 18:44:08 -0800130 push_wire_types_data=None,
131 parse_wire_types_data=None)
Rich Lane573d2b22013-12-15 13:31:27 -0800132 # Append legacy generated code
133 c_code_gen.gen_new_function_definitions(out, cls)
134 c_code_gen.gen_accessor_definitions(out, cls)
135
Rich Lanedef2e512013-12-15 15:54:02 -0800136def generate_classes_header(install_dir):
137 # Collect legacy code
138 tmp = StringIO()
139 c_code_gen.gen_struct_typedefs(tmp)
140 c_code_gen.gen_new_function_declarations(tmp)
141 c_code_gen.gen_accessor_declarations(tmp)
Rich Lanedef2e512013-12-15 15:54:02 -0800142 c_code_gen.gen_generics(tmp)
143
144 with template_utils.open_output(install_dir, "loci/inc/loci/loci_classes.h") as out:
145 util.render_template(out, "loci_classes.h",
146 legacy_code=tmp.getvalue())
147
Rich Lane8c4c23f2013-12-15 13:22:13 -0800148def generate_lists(install_dir):
149 for cls in of_g.ordered_list_objects:
150 with template_utils.open_output(install_dir, "loci/src/%s.c" % cls) as out:
Rich Lane22811f52013-12-15 15:28:03 -0800151 util.render_template(out, "class.c",
Rich Laneb9fab4b2014-03-04 18:44:08 -0800152 push_wire_types_data=None,
153 parse_wire_types_data=None)
Rich Lane8c4c23f2013-12-15 13:22:13 -0800154 # Append legacy generated code
Rich Laneb604e332013-12-15 13:23:51 -0800155 c_code_gen.gen_new_function_definitions(out, cls)
Rich Lane8c4c23f2013-12-15 13:22:13 -0800156 c_code_gen.gen_list_accessors(out, cls)
Rich Lane8a822732013-12-15 14:06:32 -0800157
158def generate_strings(install_dir):
159 object_id_strs = []
160 object_id_strs.append("of_object")
161 object_id_strs.extend(of_g.ordered_messages)
162 object_id_strs.extend(of_g.ordered_non_messages)
163 object_id_strs.extend(of_g.ordered_list_objects)
164 object_id_strs.extend(of_g.ordered_pseudo_objects)
165 object_id_strs.append("of_unknown_object")
166
167 with template_utils.open_output(install_dir, "loci/src/loci_strings.c") as out:
168 util.render_template(out, "loci_strings.c", object_id_strs=object_id_strs)
Rich Lanecce961d2013-12-15 14:20:42 -0800169
170def generate_init_map(install_dir):
171 with template_utils.open_output(install_dir, "loci/src/loci_init_map.c") as out:
172 util.render_template(out, "loci_init_map.c", classes=of_g.standard_class_order)
Rich Lanec0e20ff2013-12-15 23:40:31 -0800173
174def generate_type_maps(install_dir):
175 # Collect legacy code
176 tmp = StringIO()
177 c_type_maps.gen_type_to_obj_map_functions(tmp)
178 c_type_maps.gen_type_maps(tmp)
179 c_type_maps.gen_length_array(tmp)
180 c_type_maps.gen_extra_length_array(tmp)
181
182 with template_utils.open_output(install_dir, "loci/src/of_type_maps.c") as out:
183 util.render_template(out, "of_type_maps.c", legacy_code=tmp.getvalue())