| #!/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(os.path.basename(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('~')] |
| |
| # Read input files |
| for filename in filenames: |
| log("Processing struct file: " + filename) |
| ofinput = process_input_file(filename) |
| |
| for wire_version in ofinput.wire_versions: |
| version = loxi_globals.OFVersions.from_wire(wire_version) |
| if version in loxi_globals.OFVersions.target_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) |
| |
| log("\nGenerating files for target language %s\n" % options.lang) |
| |
| loxi_globals.OFVersions.target_versions = target_versions |
| inputs = read_input() |
| build_ir(inputs) |
| lang_module.generate(options.install_dir) |