blob: fa3a79b531c6810bee2d40284444d2d5a4470db0 [file] [log] [blame]
# Copyright 2013, Big Switch Networks, Inc.
#
# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
# the following special exception:
#
# LOXI Exception
#
# As a special exception to the terms of the EPL, you may distribute libraries
# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
# that copyright and licensing notices generated by LoxiGen are not altered or removed
# from the LoxiGen Libraries and the notice provided below is (i) included in
# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
# documentation for the LoxiGen Libraries, if distributed in binary form.
#
# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
#
# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
# a copy of the EPL at:
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# EPL for the specific language governing permissions and limitations
# under the EPL.
"""
Code generation
These functions extract data from the IR and render templates with it.
"""
from collections import namedtuple
from itertools import groupby
from StringIO import StringIO
import template_utils
from generic_utils import chunks
import loxi_globals
import loxi_ir.ir as ir
import util
import c_code_gen
import c_gen.of_g_legacy as of_g
import c_gen.type_maps as type_maps
import c_gen.c_type_maps as c_type_maps
import loxi_utils.loxi_utils as loxi_utils
import c_gen.loxi_utils_legacy as loxi_utils_legacy
CLASS_CHUNK_SIZE = 32
PushWireTypesData = namedtuple('PushWireTypesData',
['class_name', 'versioned_type_members'])
PushWireTypesMember = namedtuple('PushWireTypesMember',
['name', 'offset', 'length', 'value'])
def push_wire_types_data(uclass):
if uclass.virtual or not uclass.has_type_members:
return None
# Generate a dict of version -> list of PushWireTypesMember
type_members_by_version = {}
for version, ofclass in sorted(uclass.version_classes.items()):
pwtms = []
for m in ofclass.members:
if isinstance(m, ir.OFTypeMember):
if m.name == "version" and m.value == version.wire_version:
# Special case for version
pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, "obj->version"))
else:
pwtms.append(PushWireTypesMember(m.name, m.offset, m.length, hex(m.value)))
type_members_by_version[version] = pwtms
# Merge versions with identical type members
all_versions = sorted(type_members_by_version.keys())
versioned_type_members = []
for pwtms, versions in groupby(all_versions, type_members_by_version.get):
versioned_type_members.append((pwtms, list(versions)))
return PushWireTypesData(
class_name=uclass.name,
versioned_type_members=versioned_type_members)
ParseWireTypesData = namedtuple('ParseWireTypesData',
['class_name', 'versioned'])
ParseWireTypesVersion = namedtuple('ParseWireTypesVersion',
['discriminator', 'subclasses'])
ParseWireTypesSubclass = namedtuple('ParseWireTypesSubclass',
['class_name', 'value', 'virtual'])
def parse_wire_types_data(uclass):
if not uclass.virtual:
return None
# Generate a dict of version -> ParseWireTypesVersion
versioned = {}
for version, ofclass in sorted(uclass.version_classes.items()):
discriminator = ofclass.discriminator
subclasses = [ParseWireTypesSubclass(class_name=subclass.name,
value=subclass.member_by_name(discriminator.name).value,
virtual=subclass.virtual)
for subclass in ofclass.protocol.classes if subclass.superclass and subclass.superclass.name == ofclass.name]
subclasses.sort(key=lambda x: x.value)
versioned[version] = ParseWireTypesVersion(discriminator=discriminator,
subclasses=subclasses)
return ParseWireTypesData(class_name=uclass.name,
versioned=sorted(versioned.items()))
# Output multiple LOCI classes into each C file. This reduces the overhead of
# parsing header files, which takes longer than compiling the actual code
# for many classes. It also reduces the compiled code size.
def generate_classes(install_dir):
for i, chunk in enumerate(chunks(loxi_globals.unified.classes, CLASS_CHUNK_SIZE)):
with template_utils.open_output(install_dir, "loci/src/class%02d.c" % i) as out:
for uclass in chunk:
util.render_template(out, "class.c",
push_wire_types_data=push_wire_types_data(uclass),
parse_wire_types_data=parse_wire_types_data(uclass))
# Append legacy generated code
c_code_gen.gen_new_function_definitions(out, uclass.name)
c_code_gen.gen_accessor_definitions(out, uclass.name)
# TODO remove header classes and use the corresponding class instead
def generate_header_classes(install_dir):
for cls in of_g.standard_class_order:
if not cls.endswith("_header") or cls in ["of_header", "of_bsn_header", "of_nicira_header"]:
continue
with template_utils.open_output(install_dir, "loci/src/%s.c" % cls) as out:
util.render_template(out, "class.c",
push_wire_types_data=None,
parse_wire_types_data=None)
# Append legacy generated code
c_code_gen.gen_new_function_definitions(out, cls)
c_code_gen.gen_accessor_definitions(out, cls)
def generate_classes_header(install_dir):
# Collect legacy code
tmp = StringIO()
c_code_gen.gen_struct_typedefs(tmp)
c_code_gen.gen_new_function_declarations(tmp)
c_code_gen.gen_accessor_declarations(tmp)
c_code_gen.gen_generics(tmp)
with template_utils.open_output(install_dir, "loci/inc/loci/loci_classes.h") as out:
util.render_template(out, "loci_classes.h",
legacy_code=tmp.getvalue())
def generate_lists(install_dir):
# Collect all the lists in use
list_oftypes = set()
for uclass in loxi_globals.unified.classes:
for ofclass in uclass.version_classes.values():
for m in ofclass.members:
if isinstance(m, ir.OFDataMember) and \
loxi_utils.oftype_is_list(m.oftype):
list_oftypes.add(m.oftype)
for oftype in sorted(list(list_oftypes)):
cls, e_cls = loxi_utils_legacy.list_name_extract(oftype)
e_cls = e_cls[:-2]
e_uclass = loxi_globals.unified.class_by_name(e_cls)
with template_utils.open_output(install_dir, "loci/src/%s.c" % cls) as out:
util.render_template(out, "list.c", cls=cls, e_cls=e_cls, e_uclass=e_uclass,
wire_length_get=class_metadata_dict[e_cls].wire_length_get)
# Append legacy generated code
c_code_gen.gen_new_function_definitions(out, cls)
def generate_strings(install_dir):
object_id_strs = []
object_id_strs.append("of_object")
object_id_strs.extend(of_g.ordered_messages)
object_id_strs.extend(of_g.ordered_non_messages)
object_id_strs.extend(of_g.ordered_list_objects)
object_id_strs.extend(of_g.ordered_pseudo_objects)
object_id_strs.append("of_unknown_object")
with template_utils.open_output(install_dir, "loci/src/loci_strings.c") as out:
util.render_template(out, "loci_strings.c", object_id_strs=object_id_strs)
def generate_init_map(install_dir):
with template_utils.open_output(install_dir, "loci/src/loci_init_map.c") as out:
util.render_template(out, "loci_init_map.c", classes=of_g.standard_class_order)
def generate_type_maps(install_dir):
# Collect legacy code
tmp = StringIO()
c_type_maps.gen_length_array(tmp)
c_type_maps.gen_extra_length_array(tmp)
with template_utils.open_output(install_dir, "loci/src/of_type_maps.c") as out:
util.render_template(out, "of_type_maps.c", legacy_code=tmp.getvalue())
ClassMetadata = namedtuple('ClassMetadata',
['name', 'wire_length_get', 'wire_length_set', 'wire_type_get', 'wire_type_set'])
class_metadata = []
class_metadata_dict = {}
# These classes have handwritten C code to get/set their length fields
# See templates/of_type_maps.c
special_length_classes = set([
'of_packet_queue', 'of_meter_stats', 'of_port_desc',
'of_port_stats_entry', 'of_queue_stats_entry',
])
def build_class_metadata():
for uclass in loxi_globals.unified.classes:
wire_length_get = 'NULL'
wire_length_set = 'NULL'
wire_type_get = 'NULL'
wire_type_set = 'NULL'
if uclass and not uclass.virtual and uclass.has_type_members:
wire_type_set = '%s_push_wire_types' % uclass.name
root = uclass.inheritance_root()
if root and root.name != 'of_header':
wire_type_get = root.name + '_wire_object_id_get'
if uclass.is_message:
wire_length_get = 'of_object_message_wire_length_get'
wire_length_set = 'of_object_message_wire_length_set'
elif uclass.is_oxm:
wire_length_get = 'of_oxm_wire_length_get'
elif uclass.name in special_length_classes:
wire_length_get = '%s_wire_length_get' % uclass.name
wire_length_set = '%s_wire_length_set' % uclass.name
elif loxi_utils_legacy.class_is_tlv16(uclass.name):
wire_length_set = 'of_tlv16_wire_length_set'
wire_length_get = 'of_tlv16_wire_length_get'
elif loxi_utils_legacy.class_is_u16_len(uclass.name):
wire_length_get = "of_u16_len_wire_length_get"
wire_length_set = "of_u16_len_wire_length_set"
class_metadata.append(ClassMetadata(
name=uclass.name,
wire_length_get=wire_length_get,
wire_length_set=wire_length_set,
wire_type_get=wire_type_get,
wire_type_set=wire_type_set))
# If this is the root of an inheritance hierachy, add metadata
# for the corresponding header class
if uclass.name in type_maps.inheritance_map:
class_metadata.append(ClassMetadata(
name=uclass.name + '_header',
wire_length_get=wire_length_get,
wire_length_set=wire_length_set,
wire_type_get=wire_type_get,
wire_type_set=wire_type_set))
for metadata in class_metadata:
class_metadata_dict[metadata.name] = metadata
def generate_class_metadata(install_dir):
with template_utils.open_output(install_dir, "loci/inc/loci/loci_class_metadata.h") as out:
util.render_template(out, "loci_class_metadata.h")
with template_utils.open_output(install_dir, "loci/src/loci_class_metadata.c") as out:
util.render_template(out, "loci_class_metadata.c", class_metadata=class_metadata)