blob: ba622888617985ef40229c2a1f345fc6f68569b4 [file] [log] [blame]
#!/usr/bin/env python
# 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.
"""
@brief
Process openflow header files to create language specific LOXI interfaces
First cut at simple python script for processing input files
Internal notes
An input file for each supported OpenFlow version is passed in
on the command line.
Expected input file format:
These will probably be collapsed into a python dict or something
The first line has the ofC version identifier and nothing else
The second line has the openflow wire protocol value and nothing else
The main content is struct elements for each OF recognized class.
These are taken from current versions of openflow.h but are modified
a bit. See Overview for more information.
Class canonical form: A list of entries, each of which is a
pair "type, name;". The exception is when type is the keyword
'list' in which the syntax is "list(type) name;".
From this, internal representations are generated: For each
version, a dict indexed by class name. One element (members) is
an array giving the member name and type. From this, wire offsets
can be calculated.
@fixme Clean up the lang module architecture. It should provide a
list of files that it wants to generate and maps to the filenames,
subdirectory names and generation functions. It should also be
defined as a class, probably with the constructor taking the
language target.
@fixme Clean up global data structures such as versions and of_g
structures. They should probably be a class or classes as well.
"""
from collections import OrderedDict, defaultdict
import copy
import glob
from optparse import OptionParser
import os
import re
import string
import sys
import cmdline
from loxi_globals import OFVersions
import loxi_globals
import loxi_utils.loxi_utils as loxi_utils
import pyparsing
import loxi_front_end.parser as parser
import loxi_front_end.frontend as frontend
import loxi_ir
from generic_utils import *
root_dir = os.path.dirname(os.path.realpath(__file__))
def process_input_file(filename):
"""
Process an input file
Does not modify global state.
@param filename The input filename
@returns An OFInput object
"""
# Parse the input file
try:
with open(filename, 'r') as f:
ast = parser.parse(f.read())
except pyparsing.ParseBaseException as e:
print "Parse error in %s: %s" % (os.path.basename(filename), str(e))
sys.exit(1)
# Create the OFInput from the AST
try:
ofinput = frontend.create_ofinput(filename, ast)
except frontend.InputError as e:
print "Error in %s: %s" % (os.path.basename(filename), str(e))
sys.exit(1)
return ofinput
def read_input():
"""
Read in from files given on command line and update global state
@fixme Should select versions to support from command line
"""
ofinputs_by_version = defaultdict(lambda: [])
filenames = sorted(glob.glob("%s/openflow_input/*" % root_dir))
# Ignore emacs backup files
filenames = [x for x in filenames if not x.endswith('~')]
for filename in filenames:
log("Processing struct file: " + filename)
ofinput = process_input_file(filename)
for wire_version in ofinput.wire_versions:
ofinputs_by_version[wire_version].append(ofinput)
return ofinputs_by_version
def build_ir(ofinputs_by_version):
classes = []
enums = []
for wire_version, ofinputs in ofinputs_by_version.items():
version = OFVersions.from_wire(wire_version)
ofprotocol = loxi_ir.build_protocol(version, ofinputs)
loxi_globals.ir[version] = ofprotocol
loxi_globals.unified = loxi_ir.build_unified_ir(loxi_globals.ir)
################################################################
#
# Debug
#
################################################################
if __name__ == '__main__':
(options, args, target_versions) = cmdline.process_commandline()
# @fixme Use command line params to select log
logging.basicConfig(level = logging.INFO if not options.verbose else logging.DEBUG)
# Import the language file
lang_file = "lang_%s" % options.lang
lang_module = __import__(lang_file)
if hasattr(lang_module, "config_sanity_check") and not lang_module.config_sanity_check():
debug("Config sanity check failed\n")
sys.exit(1)
# If list files, just list auto-gen files to stdout and exit
if options.list_files:
for name in lang_module.targets:
print options.install_dir + '/' + name
sys.exit(0)
log("\nGenerating files for target language %s\n" % options.lang)
loxi_globals.OFVersions.target_versions = target_versions
inputs = read_input()
build_ir(inputs)
#log_all_class_info()
lang_module.generate(options.install_dir)