From 3fab8e0750ab1fe0a6b68545594033559133ca6a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 5 Sep 2008 14:11:23 +0000 Subject: QPID-1274 - Improved packaging for management agent and code generation tool git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@692450 13f79535-47bb-0310-9956-ffa450edef68 --- cpp/configure.ac | 2 + cpp/examples/qmf-agent/Makefile | 8 +- cpp/m4/python.m4 | 168 +++++ cpp/managementgen/Makefile.am | 29 +- cpp/managementgen/generate.py | 300 -------- cpp/managementgen/main.py | 52 -- cpp/managementgen/management-types.xml | 56 ++ cpp/managementgen/managementgen | 26 - cpp/managementgen/qmf-gen | 60 ++ cpp/managementgen/qmf/__init__.py | 19 + cpp/managementgen/qmf/generate.py | 300 ++++++++ cpp/managementgen/qmf/schema.py | 1208 ++++++++++++++++++++++++++++++++ cpp/managementgen/schema.py | 1208 -------------------------------- cpp/src/Makefile.am | 8 +- cpp/src/qmf.mk | 31 + 15 files changed, 1860 insertions(+), 1615 deletions(-) create mode 100644 cpp/m4/python.m4 delete mode 100755 cpp/managementgen/generate.py delete mode 100755 cpp/managementgen/main.py create mode 100644 cpp/managementgen/management-types.xml delete mode 100644 cpp/managementgen/managementgen create mode 100755 cpp/managementgen/qmf-gen create mode 100644 cpp/managementgen/qmf/__init__.py create mode 100755 cpp/managementgen/qmf/generate.py create mode 100755 cpp/managementgen/qmf/schema.py delete mode 100755 cpp/managementgen/schema.py create mode 100644 cpp/src/qmf.mk (limited to 'cpp') diff --git a/cpp/configure.ac b/cpp/configure.ac index 9c977d05d6..2b94430e29 100644 --- a/cpp/configure.ac +++ b/cpp/configure.ac @@ -318,6 +318,8 @@ if test $poller = xno; then AC_MSG_ERROR([Polling mechanism not implemented for $host]) fi +AM_PATH_PYTHON() + # Files to generate AC_CONFIG_FILES([ Makefile diff --git a/cpp/examples/qmf-agent/Makefile b/cpp/examples/qmf-agent/Makefile index 0b37f1a295..6c4d9a867e 100644 --- a/cpp/examples/qmf-agent/Makefile +++ b/cpp/examples/qmf-agent/Makefile @@ -27,12 +27,10 @@ CC = gcc LIB_DIR = $(QPID_DIR)/cpp/src/.libs CC_INCLUDES = -I$(SRC_DIR) -I$(QPID_DIR)/cpp/src -I$(QPID_DIR)/cpp/src/gen -I$(GEN_DIR) CC_FLAGS = -g -O3 -LD_FLAGS = -lqpidclient -lqpidcommon -L$(LIB_DIR) +LD_FLAGS = -lqmfagent -L$(LIB_DIR) SPEC_DIR = $(QPID_DIR)/specs -TYPE_FILE = $(SPEC_DIR)/management-types.xml MGEN_DIR = $(QPID_DIR)/cpp/managementgen -TEMPLATE_DIR = $(MGEN_DIR)/templates -MGEN = $(MGEN_DIR)/main.py +MGEN = $(MGEN_DIR)/qmf-gen vpath %.cpp $(SRC_DIR):$(GEN_DIR) vpath %.d $(OBJ_DIR) @@ -53,7 +51,7 @@ all: gen @$(MAKE) gen: - $(MGEN) $(SCHEMA_FILE) $(TYPE_FILE) $(TEMPLATE_DIR) $(GEN_DIR) + $(MGEN) $(SCHEMA_FILE) $(GEN_DIR) clean: rm -rf $(GEN_DIR) $(OUT_FILE) *.d *.o diff --git a/cpp/m4/python.m4 b/cpp/m4/python.m4 new file mode 100644 index 0000000000..229fd5547b --- /dev/null +++ b/cpp/m4/python.m4 @@ -0,0 +1,168 @@ +## ------------------------ -*- Autoconf -*- +## Python file handling +## From Andrew Dalke +## Updated by James Henstridge +## ------------------------ +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 1.5 are not + dnl supported because the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5. + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], + [python python2 python2.5 python2.4 python2.3 python2.2 dnl +python2.1 python2.0 python1.6 python1.5]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null || + echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null || + echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# hexversion has been introduced in Python 1.5.2; it's probably not +# worth to support older versions (1.5.1 was released on October 31, 1998). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('$2', '.')) + [[0, 0, 0]] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) diff --git a/cpp/managementgen/Makefile.am b/cpp/managementgen/Makefile.am index 993eeb1d20..7a415b4be6 100644 --- a/cpp/managementgen/Makefile.am +++ b/cpp/managementgen/Makefile.am @@ -1,8 +1,8 @@ -managementgendir = $(datadir)/managementgen -dist_managementgen_SCRIPTS = \ - main.py -nobase_managementgen_DATA = \ - schema.py generate.py \ +qmfdatadir = $(datadir)/qmf +qmfpythondir = $(pythondir) +dist_bin_SCRIPTS = \ + qmf-gen +nobase_qmfdata_DATA = \ templates/Args.h \ templates/Class.cpp \ templates/Class.h \ @@ -10,18 +10,9 @@ nobase_managementgen_DATA = \ templates/Package.cpp \ templates/Package.h \ management-types.xml +nobase_qmfpython_DATA = \ + qmf/__init__.py \ + qmf/generate.py \ + qmf/schema.py -dist_bin_SCRIPTS = managementgen - -EXTRA_DIST = $(nobase_managementgen_DATA) - -# This should depend on ../../specs/management-types.xml, but can't -# because it won't exist in a dist. This rule means that dist-gzip -# (and rpmbuild) cannot be run purely on the cpp/ directory, the -# cpp/../specs/ directory must exist, which is not a new dependency -# but this is an additional instance of the dependency. -# -# WARNING: Because this target does not have a proper dependency -# changes to the specs/management-types.xml will not be picked up! -management-types.xml: - cp $(top_srcdir)/../specs/management-types.xml . +EXTRA_DIST = $(nobase_qmfdata_DATA) $(nobase_qmfpython_DATA) diff --git a/cpp/managementgen/generate.py b/cpp/managementgen/generate.py deleted file mode 100755 index 6024173f67..0000000000 --- a/cpp/managementgen/generate.py +++ /dev/null @@ -1,300 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# 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 License for the -# specific language governing permissions and limitations -# under the License. -# - -from xml.dom.minidom import parse, parseString, Node -from cStringIO import StringIO -from stat import * -from errno import * -import os -import os.path -import filecmp - -class Template: - """ - Expandable File Template - This class is instantiated each time a - template is to be expanded. It is instantiated with the "filename" - which is the full path to the template file and the "handler" which - is an object that is responsible for storing variables (setVariable), - checking conditions (testCondition), and expanding tags (substHandler). - """ - def __init__ (self, filename, handler): - self.filename = filename - self.handler = handler - self.handler.initExpansion () - self.writing = True - - def expandLine (self, line, stream, object): - cursor = 0 - while 1: - sub = line.find ("/*MGEN:", cursor) - if sub == -1: - if self.writing: - stream.write (line[cursor:len (line)]) - return - - subend = line.find("*/", sub) - if self.writing: - stream.write (line[cursor:sub]) - cursor = subend + 2 - - tag = line[sub:subend] - - if tag[7:10] == "IF(": - close = tag.find(")") - if close == -1: - raise ValueError ("Missing ')' on condition") - cond = tag[10:close] - dotPos = cond.find (".") - if dotPos == -1: - raise ValueError ("Invalid condition tag: %s" % cond) - tagObject = cond[0:dotPos] - tagName = cond[dotPos + 1 : len(cond)] - if not self.handler.testCondition(object, tagObject, tagName): - self.writing = False - - elif tag[7:12] == "ENDIF": - self.writing = True - - else: - equalPos = tag.find ("=") - if equalPos == -1: - dotPos = tag.find (".") - if dotPos == -1: - raise ValueError ("Invalid tag: %s" % tag) - tagObject = tag[7:dotPos] - tagName = tag[dotPos + 1:len (tag)] - if self.writing: - self.handler.substHandler (object, stream, tagObject, tagName) - else: - tagKey = tag[7:equalPos] - tagVal = tag[equalPos + 1:len (tag)] - if self.writing: - self.handler.setVariable (tagKey, tagVal) - - def expand (self, object): - fd = open (self.filename) - stream = StringIO () - - for line in fd: - self.expandLine (line, stream, object) - fd.close () - - return stream - - -class Makefile: - """ Object representing a makefile fragment """ - def __init__ (self, filelists, templateFiles): - self.filelists = filelists - self.templateFiles = templateFiles - - def genGenSources (self, stream, variables): - mdir = variables["mgenDir"] - sdir = variables["specDir"] - stream.write (mdir + "/main.py \\\n") - stream.write (" " + mdir + "/generate.py \\\n") - stream.write (" " + mdir + "/schema.py \\\n") - stream.write (" " + sdir + "/management-types.xml \\\n") - stream.write (" " + sdir + "/management-schema.xml \\\n") - first = True - for template in self.templateFiles: - if first: - first = False - stream.write (" ") - else: - stream.write (" \\\n ") - stream.write (mdir + "/templates/" + template) - - def genGenCppFiles (self, stream, variables): - first = True - for file in self.filelists["cpp"]: - if first: - first = False - else: - stream.write (" \\\n ") - stream.write (file) - - def genGenHFiles (self, stream, variables): - first = True - for file in self.filelists["h"]: - if first: - first = False - else: - stream.write (" \\\n ") - stream.write (file) - - -class Generator: - """ - This class manages code generation using template files. It is instantiated - once for an entire code generation session. - """ - def createPath (self, path): - exists = True - try: - mode = os.stat (path)[ST_MODE] - except OSError, (err,text): - if err == ENOENT: - exists = False - else: - raise - if exists and not S_ISDIR (mode): - raise ValueError ("path is not directory: %s" % path) - if not exists: - pair = os.path.split (path) - if pair[0] != '': - self.createPath (pair[0]) - os.mkdir (path) - - def normalize (self, path): - newpath = os.path.normcase (os.path.normpath (path)) - self.createPath (newpath) - return newpath + "/" - - def __init__ (self, destDir, templateDir): - self.dest = self.normalize (destDir) - self.input = self.normalize (templateDir) - self.filelists = {} - self.filelists["h"] = [] - self.filelists["cpp"] = [] - self.filelists["mk"] = [] - self.templateFiles = [] - self.variables = {} - - def genDisclaimer (self, stream, variables): - prefix = variables["commentPrefix"] - stream.write (prefix + " This source file was created by a code generator.\n") - stream.write (prefix + " Please do not edit.") - - def fileExt (self, path): - dot = path.rfind (".") - if dot == -1: - return "" - return path[dot + 1:] - - def writeIfChanged (self, stream, target, force=False): - ext = self.fileExt (target) - self.filelists[ext].append (target) - tempFile = self.dest + "gen.tmp" - fd = open (tempFile, "w") - fd.write (stream.getvalue ()) - fd.close () - - try: - if not force and filecmp.cmp (target, tempFile): - os.remove (tempFile) - return - except: - pass - - try: - os.remove (target) - except: - pass - - os.rename (tempFile, target) - print "Generated:", target - - def targetPackageFile (self, schema, templateFile): - dot = templateFile.find(".") - if dot == -1: - raise ValueError ("Invalid template file name %s" % templateFile) - extension = templateFile[dot:len (templateFile)] - path = self.dest + "Package" + schema.getPackageNameCap() + extension - return path - - def targetClassFile (self, _class, templateFile): - dot = templateFile.find(".") - if dot == -1: - raise ValueError ("Invalid template file name %s" % templateFile) - extension = templateFile[dot:len (templateFile)] - path = self.dest + _class.getNameCap () + extension - return path - - def targetMethodFile (self, method, templateFile): - """ Return the file name for a method file """ - dot = templateFile.rfind(".") - if dot == -1: - raise ValueError ("Invalid template file name %s" % templateFile) - extension = templateFile[dot:] - path = self.dest + "Args" + method.getFullName () + extension - return path - - def initExpansion (self): - self.variables = {} - - def substHandler (self, object, stream, tagObject, tag): - if tagObject == "Root": - obj = "self" - else: - obj = "object" # MUST be the same as the 2nd formal parameter - - call = obj + ".gen" + tag + "(stream, self.variables)" - eval (call) - - def testCondition (self, object, tagObject, tag): - if tagObject == "Root": - obj = "self" - else: - obj = "object" # MUST be the same as the 2nd formal parameter - - call = obj + ".test" + tag + "(self.variables)" - return eval (call) - - def setVariable (self, key, value): - self.variables[key] = value - - def makeClassFiles (self, templateFile, schema, force=False): - """ Generate an expanded template per schema class """ - classes = schema.getClasses () - template = Template (self.input + templateFile, self) - self.templateFiles.append (templateFile) - for _class in classes: - target = self.targetClassFile (_class, templateFile) - stream = template.expand (_class) - self.writeIfChanged (stream, target, force) - - def makeMethodFiles (self, templateFile, schema, force=False): - """ Generate an expanded template per method-with-arguments """ - classes = schema.getClasses () - template = Template (self.input + templateFile, self) - self.templateFiles.append (templateFile) - for _class in classes: - methods = _class.getMethods () - for method in methods: - if method.getArgCount () > 0: - target = self.targetMethodFile (method, templateFile) - stream = template.expand (method) - self.writeIfChanged (stream, target, force) - - def makePackageFile (self, templateFile, schema, force=False): - """ Generate a package-specific file """ - template = Template (self.input + templateFile, self) - self.templateFiles.append (templateFile) - target = self.targetPackageFile (schema, templateFile) - stream = template.expand (schema) - self.writeIfChanged (stream, target, force) - - def makeSingleFile (self, templateFile, target, force=False): - """ Generate a single expanded template """ - makefile = Makefile (self.filelists, self.templateFiles) - template = Template (self.input + templateFile, self) - self.templateFiles.append (templateFile) - stream = template.expand (makefile) - self.writeIfChanged (stream, target, force) diff --git a/cpp/managementgen/main.py b/cpp/managementgen/main.py deleted file mode 100755 index 4459177a53..0000000000 --- a/cpp/managementgen/main.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# 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 License for the -# specific language governing permissions and limitations -# under the License. -# - -from schema import PackageSchema, SchemaClass -from generate import Generator -from optparse import OptionParser - -# Set command line options -usage = "usage: %prog [options] schema-document type-document template-directory out-directory" -parser = OptionParser (usage=usage) -parser.add_option ("-m", "--makefile", dest="makefile", metavar="FILE", - help="Makefile fragment") - -(opts, args) = parser.parse_args () - -if len (args) < 4: - parser.error ("Too few arguments") - -schemafile = args[0] -typefile = args[1] -templatedir = args[2] -outdir = args[3] - -gen = Generator (outdir, templatedir) -schema = PackageSchema (typefile, schemafile, opts) - -gen.makeClassFiles ("Class.h", schema) -gen.makeClassFiles ("Class.cpp", schema) -gen.makeMethodFiles ("Args.h", schema) -gen.makePackageFile ("Package.h", schema) -gen.makePackageFile ("Package.cpp", schema) - -if opts.makefile != None: - gen.makeSingleFile ("Makefile.mk", opts.makefile, force=True) diff --git a/cpp/managementgen/management-types.xml b/cpp/managementgen/management-types.xml new file mode 100644 index 0000000000..31337b23bc --- /dev/null +++ b/cpp/managementgen/management-types.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpp/managementgen/managementgen b/cpp/managementgen/managementgen deleted file mode 100644 index 55ea846270..0000000000 --- a/cpp/managementgen/managementgen +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# 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 License for the -# specific language governing permissions and limitations -# under the License. -# - -import sys - -sys.path.append("/usr/share/managementgen") - -import main diff --git a/cpp/managementgen/qmf-gen b/cpp/managementgen/qmf-gen new file mode 100755 index 0000000000..a29a4074fd --- /dev/null +++ b/cpp/managementgen/qmf-gen @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# 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 License for the +# specific language governing permissions and limitations +# under the License. +# +import sys +import os +from qmf.schema import PackageSchema, SchemaClass +from qmf.generate import Generator +from optparse import OptionParser + +dataPath = os.path.dirname(sys.argv[0]) + +# Set command line options +usage = "usage: %prog [options] schema-document out-directory" +parser = OptionParser (usage=usage) +parser.add_option ("-m", "--makefile", dest="makefile", metavar="FILE", + help="Makefile fragment") +parser.add_option ("-t", "--typefile", dest="typefile", metavar="FILE", default=dataPath + "/management-types.xml", + help="Type descriptor file") +parser.add_option ("-d", "--templatedir", dest="templatedir", metavar="DIR", default=dataPath + "/templates", + help="Template directory") + +(opts, args) = parser.parse_args () + +if len (args) < 2: + parser.error ("Too few arguments") + +typefile = opts.typefile +templatedir = opts.templatedir + +schemafile = args[0] +outdir = args[1] + +gen = Generator (outdir, templatedir) +schema = PackageSchema (typefile, schemafile, opts) + +gen.makeClassFiles ("Class.h", schema) +gen.makeClassFiles ("Class.cpp", schema) +gen.makeMethodFiles ("Args.h", schema) +gen.makePackageFile ("Package.h", schema) +gen.makePackageFile ("Package.cpp", schema) + +if opts.makefile != None: + gen.makeSingleFile ("Makefile.mk", opts.makefile, force=True) diff --git a/cpp/managementgen/qmf/__init__.py b/cpp/managementgen/qmf/__init__.py new file mode 100644 index 0000000000..caef6cc58b --- /dev/null +++ b/cpp/managementgen/qmf/__init__.py @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# 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 License for the +# specific language governing permissions and limitations +# under the License. +# + diff --git a/cpp/managementgen/qmf/generate.py b/cpp/managementgen/qmf/generate.py new file mode 100755 index 0000000000..c1edf0b8e2 --- /dev/null +++ b/cpp/managementgen/qmf/generate.py @@ -0,0 +1,300 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# 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 License for the +# specific language governing permissions and limitations +# under the License. +# + +from xml.dom.minidom import parse, parseString, Node +from cStringIO import StringIO +from stat import * +from errno import * +import os +import os.path +import filecmp + +class Template: + """ + Expandable File Template - This class is instantiated each time a + template is to be expanded. It is instantiated with the "filename" + which is the full path to the template file and the "handler" which + is an object that is responsible for storing variables (setVariable), + checking conditions (testCondition), and expanding tags (substHandler). + """ + def __init__ (self, filename, handler): + self.filename = filename + self.handler = handler + self.handler.initExpansion () + self.writing = True + + def expandLine (self, line, stream, object): + cursor = 0 + while 1: + sub = line.find ("/*MGEN:", cursor) + if sub == -1: + if self.writing: + stream.write (line[cursor:len (line)]) + return + + subend = line.find("*/", sub) + if self.writing: + stream.write (line[cursor:sub]) + cursor = subend + 2 + + tag = line[sub:subend] + + if tag[7:10] == "IF(": + close = tag.find(")") + if close == -1: + raise ValueError ("Missing ')' on condition") + cond = tag[10:close] + dotPos = cond.find (".") + if dotPos == -1: + raise ValueError ("Invalid condition tag: %s" % cond) + tagObject = cond[0:dotPos] + tagName = cond[dotPos + 1 : len(cond)] + if not self.handler.testCondition(object, tagObject, tagName): + self.writing = False + + elif tag[7:12] == "ENDIF": + self.writing = True + + else: + equalPos = tag.find ("=") + if equalPos == -1: + dotPos = tag.find (".") + if dotPos == -1: + raise ValueError ("Invalid tag: %s" % tag) + tagObject = tag[7:dotPos] + tagName = tag[dotPos + 1:len (tag)] + if self.writing: + self.handler.substHandler (object, stream, tagObject, tagName) + else: + tagKey = tag[7:equalPos] + tagVal = tag[equalPos + 1:len (tag)] + if self.writing: + self.handler.setVariable (tagKey, tagVal) + + def expand (self, object): + fd = open (self.filename) + stream = StringIO () + + for line in fd: + self.expandLine (line, stream, object) + fd.close () + + return stream + + +class Makefile: + """ Object representing a makefile fragment """ + def __init__ (self, filelists, templateFiles): + self.filelists = filelists + self.templateFiles = templateFiles + + def genGenSources (self, stream, variables): + mdir = variables["mgenDir"] + sdir = variables["specDir"] + stream.write (mdir + "/qmf-gen \\\n") + stream.write (" " + mdir + "/qmf/generate.py \\\n") + stream.write (" " + mdir + "/qmf/schema.py \\\n") + stream.write (" " + mdir + "/management-types.xml \\\n") + stream.write (" " + sdir + "/management-schema.xml \\\n") + first = True + for template in self.templateFiles: + if first: + first = False + stream.write (" ") + else: + stream.write (" \\\n ") + stream.write (mdir + "/templates/" + template) + + def genGenCppFiles (self, stream, variables): + first = True + for file in self.filelists["cpp"]: + if first: + first = False + else: + stream.write (" \\\n ") + stream.write (file) + + def genGenHFiles (self, stream, variables): + first = True + for file in self.filelists["h"]: + if first: + first = False + else: + stream.write (" \\\n ") + stream.write (file) + + +class Generator: + """ + This class manages code generation using template files. It is instantiated + once for an entire code generation session. + """ + def createPath (self, path): + exists = True + try: + mode = os.stat (path)[ST_MODE] + except OSError, (err,text): + if err == ENOENT: + exists = False + else: + raise + if exists and not S_ISDIR (mode): + raise ValueError ("path is not directory: %s" % path) + if not exists: + pair = os.path.split (path) + if pair[0] != '': + self.createPath (pair[0]) + os.mkdir (path) + + def normalize (self, path): + newpath = os.path.normcase (os.path.normpath (path)) + self.createPath (newpath) + return newpath + "/" + + def __init__ (self, destDir, templateDir): + self.dest = self.normalize (destDir) + self.input = self.normalize (templateDir) + self.filelists = {} + self.filelists["h"] = [] + self.filelists["cpp"] = [] + self.filelists["mk"] = [] + self.templateFiles = [] + self.variables = {} + + def genDisclaimer (self, stream, variables): + prefix = variables["commentPrefix"] + stream.write (prefix + " This source file was created by a code generator.\n") + stream.write (prefix + " Please do not edit.") + + def fileExt (self, path): + dot = path.rfind (".") + if dot == -1: + return "" + return path[dot + 1:] + + def writeIfChanged (self, stream, target, force=False): + ext = self.fileExt (target) + self.filelists[ext].append (target) + tempFile = self.dest + "gen.tmp" + fd = open (tempFile, "w") + fd.write (stream.getvalue ()) + fd.close () + + try: + if not force and filecmp.cmp (target, tempFile): + os.remove (tempFile) + return + except: + pass + + try: + os.remove (target) + except: + pass + + os.rename (tempFile, target) + print "Generated:", target + + def targetPackageFile (self, schema, templateFile): + dot = templateFile.find(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:len (templateFile)] + path = self.dest + "Package" + schema.getPackageNameCap() + extension + return path + + def targetClassFile (self, _class, templateFile): + dot = templateFile.find(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:len (templateFile)] + path = self.dest + _class.getNameCap () + extension + return path + + def targetMethodFile (self, method, templateFile): + """ Return the file name for a method file """ + dot = templateFile.rfind(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:] + path = self.dest + "Args" + method.getFullName () + extension + return path + + def initExpansion (self): + self.variables = {} + + def substHandler (self, object, stream, tagObject, tag): + if tagObject == "Root": + obj = "self" + else: + obj = "object" # MUST be the same as the 2nd formal parameter + + call = obj + ".gen" + tag + "(stream, self.variables)" + eval (call) + + def testCondition (self, object, tagObject, tag): + if tagObject == "Root": + obj = "self" + else: + obj = "object" # MUST be the same as the 2nd formal parameter + + call = obj + ".test" + tag + "(self.variables)" + return eval (call) + + def setVariable (self, key, value): + self.variables[key] = value + + def makeClassFiles (self, templateFile, schema, force=False): + """ Generate an expanded template per schema class """ + classes = schema.getClasses () + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + for _class in classes: + target = self.targetClassFile (_class, templateFile) + stream = template.expand (_class) + self.writeIfChanged (stream, target, force) + + def makeMethodFiles (self, templateFile, schema, force=False): + """ Generate an expanded template per method-with-arguments """ + classes = schema.getClasses () + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + for _class in classes: + methods = _class.getMethods () + for method in methods: + if method.getArgCount () > 0: + target = self.targetMethodFile (method, templateFile) + stream = template.expand (method) + self.writeIfChanged (stream, target, force) + + def makePackageFile (self, templateFile, schema, force=False): + """ Generate a package-specific file """ + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + target = self.targetPackageFile (schema, templateFile) + stream = template.expand (schema) + self.writeIfChanged (stream, target, force) + + def makeSingleFile (self, templateFile, target, force=False): + """ Generate a single expanded template """ + makefile = Makefile (self.filelists, self.templateFiles) + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + stream = template.expand (makefile) + self.writeIfChanged (stream, target, force) diff --git a/cpp/managementgen/qmf/schema.py b/cpp/managementgen/qmf/schema.py new file mode 100755 index 0000000000..e666bdbb39 --- /dev/null +++ b/cpp/managementgen/qmf/schema.py @@ -0,0 +1,1208 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# 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 License for the +# specific language governing permissions and limitations +# under the License. +# + +from xml.dom.minidom import parse, parseString, Node +from cStringIO import StringIO +import md5 + +#===================================================================================== +# +#===================================================================================== +class SchemaType: + def __init__ (self, node): + self.name = None + self.base = None + self.cpp = None + self.encode = None + self.decode = None + self.style = "normal" + self.accessor = None + self.init = "0" + self.perThread = False + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'base': + self.base = val + + elif key == 'cpp': + self.cpp = val + + elif key == 'encode': + self.encode = val + + elif key == 'decode': + self.decode = val + + elif key == 'style': + self.style = val + + elif key == 'accessor': + self.accessor = val + + elif key == 'init': + self.init = val + + elif key == 'perThread': + if val != 'y': + raise ValueError ("Expected 'y' in perThread attribute") + self.perThread = True + + else: + raise ValueError ("Unknown attribute in type '%s'" % key) + + if self.name == None or self.base == None or self.cpp == None or \ + self.encode == None or self.decode == None: + raise ValueError ("Missing required attribute(s) in type") + + def getName (self): + return self.name + + def genAccessor (self, stream, varName, changeFlag = None, optional = False): + if self.perThread: + prefix = "getThreadStats()->" + if self.style == "wm": + raise ValueError ("'wm' style types can't be per-thread") + else: + prefix = "" + if self.accessor == "direct": + stream.write (" inline void set_" + varName + " (" + self.cpp + " val) {\n"); + if not self.perThread: + stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") + if self.style != "mma": + stream.write (" " + prefix + varName + " = val;\n") + if optional: + stream.write (" presenceMask[presenceByte_%s] |= presenceMask_%s;\n" % (varName, varName)) + if self.style == "wm": + stream.write (" if (" + varName + "Low > val)\n") + stream.write (" " + varName + "Low = val;\n") + stream.write (" if (" + varName + "High < val)\n") + stream.write (" " + varName + "High = val;\n") + if self.style == "mma": + stream.write (" " + prefix + varName + "Count++;\n") + stream.write (" " + prefix + varName + "Total += val;\n") + stream.write (" if (" + prefix + varName + "Min > val)\n") + stream.write (" " + prefix + varName + "Min = val;\n") + stream.write (" if (" + prefix + varName + "Max < val)\n") + stream.write (" " + prefix + varName + "Max = val;\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n") + if self.style != "mma": + stream.write (" inline " + self.cpp + "& get_" + varName + "() {\n"); + if not self.perThread: + stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") + stream.write (" return " + prefix + varName + ";\n") + stream.write (" }\n") + if optional: + stream.write (" inline void clr_" + varName + "() {\n") + stream.write (" presenceMask[presenceByte_%s] &= ~presenceMask_%s;\n" % (varName, varName)) + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n") + stream.write (" inline bool isSet_" + varName + "() {\n") + stream.write (" return (presenceMask[presenceByte_%s] & presenceMask_%s) != 0;\n" % (varName, varName)) + stream.write (" }\n") + elif self.accessor == "counter": + stream.write (" inline void inc_" + varName + " (" + self.cpp + " by = 1) {\n"); + if not self.perThread: + stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") + stream.write (" " + prefix + varName + " += by;\n") + if self.style == "wm": + stream.write (" if (" + varName + "High < " + varName + ")\n") + stream.write (" " + varName + "High = " + varName + ";\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + stream.write (" inline void dec_" + varName + " (" + self.cpp + " by = 1) {\n"); + if not self.perThread: + stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") + stream.write (" " + prefix + varName + " -= by;\n") + if self.style == "wm": + stream.write (" if (" + varName + "Low > " + varName + ")\n") + stream.write (" " + varName + "Low = " + varName + ";\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + + def genHiLoStatResets (self, stream, varName): + if self.style == "wm": + stream.write (" " + varName + "High = " + varName + ";\n") + stream.write (" " + varName + "Low = " + varName + ";\n") + if self.style == "mma": + stream.write (" " + varName + "Count = 0;\n") + stream.write (" " + varName + "Total = 0;\n") + stream.write (" " + varName + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") + stream.write (" " + varName + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") + + def genPerThreadHiLoStatResets (self, stream, varName, cpptype): + if self.style == "mma": + stream.write (" threadStats->" + varName + "Count = 0;\n") + stream.write (" threadStats->" + varName + "Total = 0;\n") + stream.write (" threadStats->" + varName + "Min = std::numeric_limits<" + cpptype + ">::max();\n") + stream.write (" threadStats->" + varName + "Max = std::numeric_limits<" + cpptype + ">::min();\n") + + def genWrite (self, stream, varName, indent=" "): + if self.style != "mma": + stream.write (indent + self.encode.replace ("@", "buf").replace ("#", varName) + ";\n") + if self.style == "wm": + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "High") + ";\n") + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Low") + ";\n") + if self.style == "mma": + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count") + ";\n") + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count ? " + varName + "Min : 0") + ";\n") + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Max") + ";\n") + stream.write (indent + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count ? " + varName + "Total / " + + varName + "Count : 0") + ";\n") + + + def getReadCode (self, varName, bufName): + result = self.decode.replace ("@", bufName).replace ("#", varName) + return result + + def getWriteCode (self, varName, bufName): + result = self.encode.replace ("@", bufName).replace ("#", varName) + return result + +#===================================================================================== +# +#===================================================================================== +class TypeSpec: + def __init__ (self, file): + self.types = {} + dom = parse (file) + document = dom.documentElement + if document.tagName != 'schema-types': + raise ValueError ("Expected 'schema-types' in type file") + + for child in document.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'type': + stype = SchemaType (child) + self.types[stype.getName ()] = stype + else: + raise ValueError ("Unknown type tag '%s'" % child.nodeName) + + def getType (self, name): + return self.types[name] + + +#===================================================================================== +# +#===================================================================================== +class Type: + def __init__ (self, name, typespec): + self.type = typespec.getType (name) + +#===================================================================================== +# +#===================================================================================== +class SchemaProperty: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.ref = None + self.access = "RO" + self.isIndex = 0 + self.isParentRef = 0 + self.isGeneralRef = 0 + self.isOptional = 0 + self.unit = None + self.min = None + self.max = None + self.maxLen = None + self.desc = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = makeValidCppSymbol(val) + + elif key == 'type': + self.type = Type (val, typespec) + if self.type.type.accessor != 'direct': + raise ValueError ("Class properties must have a type with a direct accessor") + + elif key == 'references': + self.ref = val + + elif key == 'access': + self.access = val + + elif key == 'index': + if val != 'y': + raise ValueError ("Expected 'y' in index attribute") + self.isIndex = 1 + + elif key == 'parentRef': + if val != 'y': + raise ValueError ("Expected 'y' in parentRef attribute") + self.isParentRef = 1 + + elif key == 'isGeneralReference': + if val != 'y': + raise ValueError ("Expected 'y' in isGeneralReference attribute") + self.isGeneralRef = 1 + + elif key == 'optional': + if val != 'y': + raise ValueError ("Expected 'y' in optional attribute") + self.isOptional = 1 + + elif key == 'unit': + self.unit = val + + elif key == 'min': + self.min = val + + elif key == 'max': + self.max = val + + elif key == 'maxlen': + self.maxLen = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in property '%s'" % key) + + if self.access == "RC" and self.isOptional == 1: + raise ValueError ("Properties with ReadCreate access must not be optional (%s)" % self.name) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in property") + if self.type == None: + raise ValueError ("Missing 'type' attribute in property") + + def getName (self): + return self.name + + def isConstructorArg (self): + if self.access == "RC" and self.isParentRef == 0: + return 1 + return 0 + + def genDeclaration (self, stream, prefix=" "): + stream.write (prefix + self.type.type.cpp + " " + self.name + ";\n") + + def genFormalParam (self, stream, variables): + stream.write (self.type.type.cpp + " _" + self.name) + + def genAccessor (self, stream): + self.type.type.genAccessor (stream, self.name, "configChanged", self.isOptional == 1) + + def genSchema (self, stream): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + stream.write (" ft.setInt (ACCESS, ACCESS_" + self.access + ");\n") + stream.write (" ft.setInt (INDEX, " + str (self.isIndex) + ");\n") + stream.write (" ft.setInt (OPTIONAL, " + str (self.isOptional) + ");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if self.min != None: + stream.write (" ft.setInt (MIN, " + self.min + ");\n") + if self.max != None: + stream.write (" ft.setInt (MAX, " + self.max + ");\n") + if self.maxLen != None: + stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + + def genWrite (self, stream): + indent = " " + if self.isOptional: + stream.write(" if (presenceMask[presenceByte_%s] & presenceMask_%s) {\n" % (self.name, self.name)) + indent = " " + self.type.type.genWrite (stream, self.name, indent) + if self.isOptional: + stream.write(" }\n") + + +#===================================================================================== +# +#===================================================================================== +class SchemaStatistic: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.unit = None + self.desc = None + self.assign = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = makeValidCppSymbol(val) + + elif key == 'type': + self.type = Type (val, typespec) + + elif key == 'unit': + self.unit = val + + elif key == 'desc': + self.desc = val + + elif key == 'assign': + self.assign = val + + else: + raise ValueError ("Unknown attribute in statistic '%s'" % key) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in statistic") + if self.type == None: + raise ValueError ("Missing 'type' attribute in statistic") + + def getName (self): + return self.name + + def genDeclaration (self, stream, prefix=" "): + if self.type.type.style != "mma": + stream.write (prefix + self.type.type.cpp + " " + self.name + ";\n") + if self.type.type.style == 'wm': + stream.write (prefix + self.type.type.cpp + " " + self.name + "High;\n") + stream.write (prefix + self.type.type.cpp + " " + self.name + "Low;\n") + if self.type.type.style == "mma": + stream.write (prefix + self.type.type.cpp + " " + self.name + "Count;\n") + stream.write (prefix + "uint64_t " + self.name + "Total;\n") + stream.write (prefix + self.type.type.cpp + " " + self.name + "Min;\n") + stream.write (prefix + self.type.type.cpp + " " + self.name + "Max;\n") + + def genAccessor (self, stream): + self.type.type.genAccessor (stream, self.name, "instChanged") + + def genHiLoStatResets (self, stream): + self.type.type.genHiLoStatResets (stream, self.name) + + def genPerThreadHiLoStatResets (self, stream): + self.type.type.genPerThreadHiLoStatResets (stream, self.name, self.type.type.cpp) + + def genSchemaText (self, stream, name, desc): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if desc != None: + stream.write (" ft.setString (DESC, \"" + desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + + def genSchema (self, stream): + if self.type.type.style != "mma": + self.genSchemaText (stream, self.name, self.desc) + if self.type.type.style == "wm": + descHigh = self.desc + descLow = self.desc + if self.desc != None: + descHigh = descHigh + " (High)" + descLow = descLow + " (Low)" + self.genSchemaText (stream, self.name + "High", descHigh) + self.genSchemaText (stream, self.name + "Low", descLow) + if self.type.type.style == "mma": + descCount = self.desc + descMin = self.desc + descMax = self.desc + descAverage = self.desc + if self.desc != None: + descCount = descCount + " (Samples)" + descMin = descMin + " (Min)" + descMax = descMax + " (Max)" + descAverage = descAverage + " (Average)" + self.genSchemaText (stream, self.name + "Samples", descCount) + self.genSchemaText (stream, self.name + "Min", descMin) + self.genSchemaText (stream, self.name + "Max", descMax) + self.genSchemaText (stream, self.name + "Average", descAverage) + + def genAssign (self, stream): + if self.assign != None: + if self.type.type.perThread: + prefix = " threadStats->" + else: + prefix = "" + stream.write (" " + prefix + self.name + " = (" + self.type.type.cpp + + ") (" + self.assign + ");\n") + + def genWrite (self, stream): + if self.type.type.perThread: + self.type.type.genWrite (stream, "totals." + self.name) + else: + self.type.type.genWrite (stream, self.name) + + def genInitialize (self, stream, prefix="", indent=" "): + val = self.type.type.init + if self.type.type.style != "mma": + stream.write (indent + prefix + self.name + " = " + val + ";\n") + if self.type.type.style == "wm": + stream.write (indent + prefix + self.name + "High = " + val + ";\n") + stream.write (indent + prefix + self.name + "Low = " + val + ";\n") + if self.type.type.style == "mma": + stream.write (indent + prefix + self.name + "Count = 0;\n") + stream.write (indent + prefix + self.name + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") + stream.write (indent + prefix + self.name + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") + stream.write (indent + prefix + self.name + "Total = 0;\n") + + def genInitializeTotalPerThreadStats (self, stream): + if self.type.type.style == "mma": + stream.write (" totals->" + self.name + "Count = 0;\n") + stream.write (" totals->" + self.name + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") + stream.write (" totals->" + self.name + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") + stream.write (" totals->" + self.name + "Total = 0;\n") + else: + stream.write (" totals->" + self.name + " = 0;\n") + + def genAggregatePerThreadStats (self, stream): + if self.type.type.style == "mma": + stream.write (" totals->%sCount += threadStats->%sCount;\n" % (self.name, self.name)) + stream.write (" if (totals->%sMin > threadStats->%sMin)\n" % (self.name, self.name)) + stream.write (" totals->%sMin = threadStats->%sMin;\n" % (self.name, self.name)) + stream.write (" if (totals->%sMax < threadStats->%sMax)\n" % (self.name, self.name)) + stream.write (" totals->%sMax = threadStats->%sMax;\n" % (self.name, self.name)) + stream.write (" totals->%sTotal += threadStats->%sTotal;\n" % (self.name, self.name)) + else: + stream.write (" totals->%s += threadStats->%s;\n" % (self.name, self.name)) + +#===================================================================================== +# +#===================================================================================== +class SchemaArg: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.unit = None + self.dir = "I" + self.min = None + self.max = None + self.maxLen = None + self.desc = None + self.default = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = makeValidCppSymbol(val) + + elif key == 'type': + self.type = Type (val, typespec) + + elif key == 'unit': + self.unit = val + + elif key == 'dir': + self.dir = val.upper () + + elif key == 'min': + self.min = val + + elif key == 'max': + self.max = val + + elif key == 'maxlen': + self.maxLen = val + + elif key == 'desc': + self.desc = val + + elif key == 'default': + self.default = val + + else: + raise ValueError ("Unknown attribute in arg '%s'" % key) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in arg") + if self.type == None: + raise ValueError ("Missing 'type' attribute in arg") + + def getName (self): + return self.name + + def getDir (self): + return self.dir + + def genSchema (self, stream, event=False): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + if (not event): + stream.write (" ft.setString (DIR, \"" + self.dir + "\");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if not event: + if self.min != None: + stream.write (" ft.setInt (MIN, " + self.min + ");\n") + if self.max != None: + stream.write (" ft.setInt (MAX, " + self.max + ");\n") + if self.maxLen != None: + stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") + if self.default != None: + stream.write (" ft.setString (DEFAULT, \"" + self.default + "\");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + + def genFormalParam (self, stream, variables): + stream.write ("%s _%s" % (self.type.type.cpp, self.name)) + +#===================================================================================== +# +#===================================================================================== +class SchemaMethod: + def __init__ (self, parent, node, typespec): + self.parent = parent + self.name = None + self.desc = None + self.args = [] + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = makeValidCppSymbol(val) + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in method '%s'" % key) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'arg': + arg = SchemaArg (child, typespec) + self.args.append (arg) + else: + raise ValueError ("Unknown method tag '%s'" % child.nodeName) + + def getName (self): + return self.name + + def getFullName (self): + return capitalize(self.parent.getName()) + self.name[0:1].upper() +\ + self.name[1:] + + def getArgCount (self): + return len (self.args) + + #=================================================================================== + # Code Generation Functions. The names of these functions (minus the leading "gen") + # match the substitution keywords in the template files. + #=================================================================================== + def genNameUpper (self, stream, variables): + stream.write (self.getFullName ().upper ()) + + def genNameCamel (self, stream, variables): + stream.write (self.getFullName ()) + + def genArguments (self, stream, variables): + for arg in self.args: + ctype = arg.type.type.cpp + dirTag = arg.dir.lower() + "_" + stream.write (" " + ctype + " " + dirTag + arg.getName () + ";\n") + + def genSchema (self, stream, variables): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (ARGCOUNT, " + str (len (self.args)) + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + for arg in self.args: + arg.genSchema (stream) + +#===================================================================================== +# +#===================================================================================== +class SchemaEvent: + def __init__ (self, parent, node, typespec): + self.parent = parent + self.name = None + self.desc = None + self.args = [] + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in event '%s'" % key) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'arg': + arg = SchemaArg (child, typespec) + self.args.append (arg) + else: + raise ValueError ("Unknown event tag '%s'" % child.nodeName) + + def getName (self): + return self.name + + def getFullName (self): + return capitalize(self.parent.getName()) + capitalize(self.name) + + def getArgCount (self): + return len (self.args) + + def genMethodBody (self, stream, variables, classObject): + stream.write("void ") + classObject.genNameCap(stream, variables) + stream.write("::event_%s(" % self.name) + count = 0 + for arg in self.args: + arg.genFormalParam(stream, variables) + count += 1 + if count < len(self.args): + stream.write(", ") + stream.write(") {\n") + stream.write(" sys::Mutex::ScopedLock mutex(getMutex());\n") + stream.write(" Buffer* buf = startEventLH();\n") + stream.write(" objectId.encode(*buf);\n") + stream.write(" buf->putShortString(packageName);\n") + stream.write(" buf->putShortString(className);\n") + stream.write(" buf->putBin128(md5Sum);\n") + stream.write(" buf->putShortString(\"%s\");\n" % self.name) + for arg in self.args: + stream.write(" %s;\n" % arg.type.type.encode.replace("@", "(*buf)").replace("#", "_" + arg.name)) + stream.write(" finishEventLH(buf);\n") + stream.write("}\n\n") + + def genMethodDecl (self, stream, variables): + stream.write(" void event_%s(" % self.name) + count = 0 + for arg in self.args: + arg.genFormalParam(stream, variables) + count += 1 + if count < len(self.args): + stream.write(", ") + stream.write(");\n") + + def genSchema(self, stream, variables): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (ARGCOUNT, " + str (len (self.args)) + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + for arg in self.args: + arg.genSchema (stream, True) + + + +class SchemaClass: + def __init__ (self, package, node, typespec, fragments, options): + self.packageName = package + self.properties = [] + self.statistics = [] + self.methods = [] + self.events = [] + self.options = options + self.md5Sum = md5.new () + + self.hash (node) + + attrs = node.attributes + self.name = makeValidCppSymbol(attrs['name'].nodeValue) + + children = node.childNodes + for child in children: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'property': + sub = SchemaProperty (child, typespec) + self.properties.append (sub) + + elif child.nodeName == 'statistic': + sub = SchemaStatistic (child, typespec) + self.statistics.append (sub) + + elif child.nodeName == 'method': + sub = SchemaMethod (self, child, typespec) + self.methods.append (sub) + + elif child.nodeName == 'event': + sub = SchemaEvent (self, child, typespec) + self.events.append (sub) + + elif child.nodeName == 'group': + self.expandFragment (child, fragments) + + else: + raise ValueError ("Unknown class tag '%s'" % child.nodeName) + + # Adjust the 'assign' attributes for each statistic + for stat in self.statistics: + if stat.assign != None and stat.type.type.perThread: + stat.assign = self.adjust (stat.assign, self.statistics) + + def adjust (self, text, statistics): + result = text + start = 0 + while True: + next = None + for stat in statistics: + pos = result.find (stat.name, start) + if pos != -1 and (next == None or pos < next[0]): + next = (pos, stat.name) + if next == None: + return result + pos = next[0] + result = result[0:pos] + "threadStats->" + result[pos:] + start = pos + 9 + len(next[1]) + + def hash (self, node): + attrs = node.attributes + self.md5Sum.update (node.nodeName) + + for idx in range (attrs.length): + self.md5Sum.update (attrs.item(idx).nodeName) + self.md5Sum.update (attrs.item(idx).nodeValue) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + self.hash (child) + + def expandFragment (self, node, fragments): + attrs = node.attributes + name = attrs['name'].nodeValue + for fragment in fragments: + if fragment.name == name: + self.md5Sum.update (fragment.md5Sum.digest()) + for config in fragment.properties: + self.properties.append (config) + for inst in fragment.statistics: + self.statistics.append (inst) + for method in fragment.methods: + self.methods.append (method) + for event in fragment.events: + self.events.append (event) + return + raise ValueError ("Undefined group '%s'" % name) + + def getName (self): + return self.name + + def getNameCap (self): + return capitalize(self.name) + + def getMethods (self): + return self.methods + + def getEvents (self): + return self.events + + def getPackageNameCap (self): + return capitalize(self.packageName) + + #=================================================================================== + # Code Generation Functions. The names of these functions (minus the leading "gen") + # match the substitution keywords in the template files. + #=================================================================================== + def testExistOptionals (self, variables): + for prop in self.properties: + if prop.isOptional == 1: + return True + return False + + def testExistPerThreadStats (self, variables): + for inst in self.statistics: + if inst.type.type.perThread: + return True + return False + + def testExistPerThreadAssign (self, variables): + for inst in self.statistics: + if inst.type.type.perThread and inst.assign != None: + return True + return False + + def testExistPerThreadResets (self, variables): + for inst in self.statistics: + if inst.type.type.perThread and inst.type.type.style == "mma": + return True + return False + + def testNoStatistics (self, variables): + return len (self.statistics) == 0 + + def genAccessorMethods (self, stream, variables): + for config in self.properties: + if config.access != "RC": + config.genAccessor (stream) + for inst in self.statistics: + if inst.assign == None: + inst.genAccessor (stream) + + def genConfigCount (self, stream, variables): + stream.write ("%d" % len (self.properties)) + + def genConfigDeclarations (self, stream, variables): + for element in self.properties: + element.genDeclaration (stream) + + def genConstructorArgs (self, stream, variables): + # Constructor args are config elements with read-create access + result = "" + for element in self.properties: + if element.isConstructorArg (): + stream.write (", ") + element.genFormalParam (stream, variables) + + def genConstructorInits (self, stream, variables): + for element in self.properties: + if element.isConstructorArg (): + stream.write ("," + element.getName () + "(_" + element.getName () + ")") + + def genDoMethodArgs (self, stream, variables): + methodCount = 0 + inArgCount = 0 + for method in self.methods: + methodCount = methodCount + 1 + for arg in method.args: + if arg.getDir () == "I" or arg.getDir () == "IO": + inArgCount = inArgCount + 1 + + if methodCount == 0: + stream.write ("string, Buffer&, Buffer& outBuf") + else: + if inArgCount == 0: + stream.write ("string methodName, Buffer&, Buffer& outBuf") + else: + stream.write ("string methodName, Buffer& inBuf, Buffer& outBuf") + + def genEventCount (self, stream, variables): + stream.write ("%d" % len (self.events)) + + def genEventMethodBodies (self, stream, variables): + for event in self.events: + event.genMethodBody (stream, variables, self) + + def genEventMethodDecls (self, stream, variables): + for event in self.events: + event.genMethodDecl (stream, variables) + + def genEventSchema (self, stream, variables): + for event in self.events: + event.genSchema (stream, variables) + + def genHiLoStatResets (self, stream, variables): + for inst in self.statistics: + if not inst.type.type.perThread: + inst.genHiLoStatResets (stream) + + def genPerThreadHiLoStatResets (self, stream, variables): + for inst in self.statistics: + if inst.type.type.perThread: + inst.genPerThreadHiLoStatResets (stream) + + def genInitializeElements (self, stream, variables): + for inst in self.statistics: + if not inst.type.type.perThread: + inst.genInitialize (stream) + + def genInitializePerThreadElements (self, stream, variables): + for inst in self.statistics: + if inst.type.type.perThread: + inst.genInitialize (stream, "threadStats->", " ") + + def genInitializeTotalPerThreadStats (self, stream, variables): + for inst in self.statistics: + if inst.type.type.perThread: + inst.genInitializeTotalPerThreadStats (stream) + + def genAggregatePerThreadStats (self, stream, variables): + for inst in self.statistics: + if inst.type.type.perThread: + inst.genAggregatePerThreadStats (stream) + + def genInstCount (self, stream, variables): + count = 0 + for inst in self.statistics: + count = count + 1 + if inst.type.type.style == "wm": + count = count + 2 + if inst.type.type.style == "mma": + count = count + 3 + stream.write ("%d" % count) + + def genInstDeclarations (self, stream, variables): + for element in self.statistics: + if not element.type.type.perThread: + element.genDeclaration (stream) + + def genPerThreadDeclarations (self, stream, variables): + for element in self.statistics: + if element.type.type.perThread: + element.genDeclaration (stream, " ") + + def genMethodArgIncludes (self, stream, variables): + for method in self.methods: + if method.getArgCount () > 0: + stream.write ("#include \"Args" + method.getFullName () + ".h\"\n") + + def genMethodCount (self, stream, variables): + stream.write ("%d" % len (self.methods)) + + def genMethodHandlers (self, stream, variables): + for method in self.methods: + stream.write ("\n if (methodName == \"" + method.getName () + "\") {\n") + if method.getArgCount () == 0: + stream.write (" ArgsNone ioArgs;\n") + else: + stream.write (" Args" + method.getFullName () + " ioArgs;\n") + for arg in method.args: + if arg.getDir () == "I" or arg.getDir () == "IO": + stream.write (" " +\ + arg.type.type.getReadCode ("ioArgs." +\ + arg.dir.lower () + "_" +\ + arg.name, "inBuf") + ";\n") + + stream.write (" status = coreObject->ManagementMethod (METHOD_" +\ + method.getName().upper() + ", ioArgs);\n") + stream.write (" outBuf.putLong (status);\n") + stream.write (" outBuf.putShortString (Manageable::StatusText (status));\n") + for arg in method.args: + if arg.getDir () == "O" or arg.getDir () == "IO": + stream.write (" " +\ + arg.type.type.getWriteCode ("ioArgs." +\ + arg.dir.lower () + "_" +\ + arg.name, "outBuf") + ";\n") + stream.write (" return;\n }\n") + + def genPresenceMaskBytes (self, stream, variables): + count = 0 + for prop in self.properties: + if prop.isOptional == 1: + count += 1 + if count == 0: + stream.write("0") + else: + stream.write (str(((count - 1) / 8) + 1)) + + def genPresenceMaskConstants (self, stream, variables): + count = 0 + for prop in self.properties: + if prop.isOptional == 1: + stream.write(" static const uint8_t presenceByte_%s = %d;\n" % (prop.name, count / 8)) + stream.write(" static const uint8_t presenceMask_%s = %d;\n" % (prop.name, 1 << (count % 8))) + count += 1 + + def genPropertySchema (self, stream, variables): + for prop in self.properties: + prop.genSchema (stream) + + def genSetGeneralReferenceDeclaration (self, stream, variables): + for prop in self.properties: + if prop.isGeneralRef: + stream.write ("void setReference(ObjectId objectId) { " + prop.name + " = objectId; }\n") + + def genStatisticSchema (self, stream, variables): + for stat in self.statistics: + stat.genSchema (stream) + + def genMethodIdDeclarations (self, stream, variables): + number = 1 + for method in self.methods: + stream.write (" static const uint32_t METHOD_" + method.getName().upper() +\ + " = %d;\n" % number) + number = number + 1 + + def genMethodSchema (self, stream, variables): + for method in self.methods: + method.genSchema (stream, variables) + + def genNameCap (self, stream, variables): + stream.write (capitalize(self.name)) + + def genNameLower (self, stream, variables): + stream.write (self.name.lower ()) + + def genNamePackageCap (self, stream, variables): + stream.write (self.getPackageNameCap ()) + + def genNamePackageLower (self, stream, variables): + stream.write (self.packageName.lower ()) + + def genNameUpper (self, stream, variables): + stream.write (self.name.upper ()) + + def genParentArg (self, stream, variables): + for config in self.properties: + if config.isParentRef == 1: + stream.write (", Manageable* _parent") + return + + def genParentRefAssignment (self, stream, variables): + for config in self.properties: + if config.isParentRef == 1: + stream.write (config.getName () + \ + " = _parent->GetManagementObject ()->getObjectId ();") + return + + def genSchemaMD5 (self, stream, variables): + sum = self.md5Sum.digest () + for idx in range (len (sum)): + if idx != 0: + stream.write (",") + stream.write (hex (ord (sum[idx]))) + + def genAssign (self, stream, variables): + for inst in self.statistics: + if not inst.type.type.perThread: + inst.genAssign (stream) + + def genPerThreadAssign (self, stream, variables): + for inst in self.statistics: + if inst.type.type.perThread: + inst.genAssign (stream) + + def genWriteProperties (self, stream, variables): + for prop in self.properties: + prop.genWrite (stream) + + def genWriteStatistics (self, stream, variables): + for stat in self.statistics: + stat.genWrite (stream) + + + +class PackageSchema: + def __init__ (self, typefile, schemafile, options): + + self.classes = [] + self.fragments = [] + self.typespec = TypeSpec (typefile) + + dom = parse (schemafile) + document = dom.documentElement + if document.tagName != 'schema': + raise ValueError ("Expected 'schema' node") + attrs = document.attributes + self.packageName = makeValidCppSymbol(attrs['package'].nodeValue) + + children = document.childNodes + for child in children: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'class': + cls = SchemaClass (self.packageName, child, self.typespec, + self.fragments, options) + self.classes.append (cls) + + elif child.nodeName == 'group': + cls = SchemaClass (self.packageName, child, self.typespec, + self.fragments, options) + self.fragments.append (cls) + + else: + raise ValueError ("Unknown schema tag '%s'" % child.nodeName) + + def getPackageName (self): + return self.packageName + + def getPackageNameCap (self): + return capitalize(self.packageName) + + def getClasses (self): + return self.classes + + def genPackageNameUpper (self, stream, variables): + stream.write (self.packageName.upper ()) + + def genPackageNameCap (self, stream, variables): + stream.write (self.getPackageNameCap ()) + + def genClassIncludes (self, stream, variables): + for _class in self.classes: + stream.write ("#include \"") + _class.genNameCap (stream, variables) + stream.write (".h\"\n") + + def genClassRegisters (self, stream, variables): + for _class in self.classes: + stream.write (" ") + _class.genNameCap (stream, variables) + stream.write ("::registerClass(agent);\n") + + +#===================================================================================== +# Utility Functions +#===================================================================================== + +# Create a valid C++ symbol from the input string so that it can be +# used in generated C++ source. For instance, change "qpid.mgmt" to +# "qpidMgmt". +# +# Input: Raw string (str) to process +# Output: String (str) suitable for use as a C++ symbol +# +# Limitations: Currently, only strips periods ('.') from strings, +# eventually should strip :'s and ,'s and ''s, oh my! +def makeValidCppSymbol(input): + output = str() + capitalize = False + + for char in input: + skip = False + + if char == ".": + capitalize = True + skip = True + + if not skip: + output += capitalize and char.upper() or char + + capitalize = False + + return output + +# Capitalize a string by /only/ forcing the first character to be +# uppercase. The rest of the string is left alone. This is different +# from str.capitalize(), which forces the first character to uppercase +# and the rest to lowercase. +# +# Input: A string (str) to capitalize +# Output: A string (str) with the first character as uppercase +def capitalize(input): + return input[0].upper() + input[1:] diff --git a/cpp/managementgen/schema.py b/cpp/managementgen/schema.py deleted file mode 100755 index e666bdbb39..0000000000 --- a/cpp/managementgen/schema.py +++ /dev/null @@ -1,1208 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# 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 License for the -# specific language governing permissions and limitations -# under the License. -# - -from xml.dom.minidom import parse, parseString, Node -from cStringIO import StringIO -import md5 - -#===================================================================================== -# -#===================================================================================== -class SchemaType: - def __init__ (self, node): - self.name = None - self.base = None - self.cpp = None - self.encode = None - self.decode = None - self.style = "normal" - self.accessor = None - self.init = "0" - self.perThread = False - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = val - - elif key == 'base': - self.base = val - - elif key == 'cpp': - self.cpp = val - - elif key == 'encode': - self.encode = val - - elif key == 'decode': - self.decode = val - - elif key == 'style': - self.style = val - - elif key == 'accessor': - self.accessor = val - - elif key == 'init': - self.init = val - - elif key == 'perThread': - if val != 'y': - raise ValueError ("Expected 'y' in perThread attribute") - self.perThread = True - - else: - raise ValueError ("Unknown attribute in type '%s'" % key) - - if self.name == None or self.base == None or self.cpp == None or \ - self.encode == None or self.decode == None: - raise ValueError ("Missing required attribute(s) in type") - - def getName (self): - return self.name - - def genAccessor (self, stream, varName, changeFlag = None, optional = False): - if self.perThread: - prefix = "getThreadStats()->" - if self.style == "wm": - raise ValueError ("'wm' style types can't be per-thread") - else: - prefix = "" - if self.accessor == "direct": - stream.write (" inline void set_" + varName + " (" + self.cpp + " val) {\n"); - if not self.perThread: - stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") - if self.style != "mma": - stream.write (" " + prefix + varName + " = val;\n") - if optional: - stream.write (" presenceMask[presenceByte_%s] |= presenceMask_%s;\n" % (varName, varName)) - if self.style == "wm": - stream.write (" if (" + varName + "Low > val)\n") - stream.write (" " + varName + "Low = val;\n") - stream.write (" if (" + varName + "High < val)\n") - stream.write (" " + varName + "High = val;\n") - if self.style == "mma": - stream.write (" " + prefix + varName + "Count++;\n") - stream.write (" " + prefix + varName + "Total += val;\n") - stream.write (" if (" + prefix + varName + "Min > val)\n") - stream.write (" " + prefix + varName + "Min = val;\n") - stream.write (" if (" + prefix + varName + "Max < val)\n") - stream.write (" " + prefix + varName + "Max = val;\n") - if changeFlag != None: - stream.write (" " + changeFlag + " = true;\n") - stream.write (" }\n") - if self.style != "mma": - stream.write (" inline " + self.cpp + "& get_" + varName + "() {\n"); - if not self.perThread: - stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") - stream.write (" return " + prefix + varName + ";\n") - stream.write (" }\n") - if optional: - stream.write (" inline void clr_" + varName + "() {\n") - stream.write (" presenceMask[presenceByte_%s] &= ~presenceMask_%s;\n" % (varName, varName)) - if changeFlag != None: - stream.write (" " + changeFlag + " = true;\n") - stream.write (" }\n") - stream.write (" inline bool isSet_" + varName + "() {\n") - stream.write (" return (presenceMask[presenceByte_%s] & presenceMask_%s) != 0;\n" % (varName, varName)) - stream.write (" }\n") - elif self.accessor == "counter": - stream.write (" inline void inc_" + varName + " (" + self.cpp + " by = 1) {\n"); - if not self.perThread: - stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") - stream.write (" " + prefix + varName + " += by;\n") - if self.style == "wm": - stream.write (" if (" + varName + "High < " + varName + ")\n") - stream.write (" " + varName + "High = " + varName + ";\n") - if changeFlag != None: - stream.write (" " + changeFlag + " = true;\n") - stream.write (" }\n"); - stream.write (" inline void dec_" + varName + " (" + self.cpp + " by = 1) {\n"); - if not self.perThread: - stream.write (" sys::Mutex::ScopedLock mutex(accessLock);\n") - stream.write (" " + prefix + varName + " -= by;\n") - if self.style == "wm": - stream.write (" if (" + varName + "Low > " + varName + ")\n") - stream.write (" " + varName + "Low = " + varName + ";\n") - if changeFlag != None: - stream.write (" " + changeFlag + " = true;\n") - stream.write (" }\n"); - - def genHiLoStatResets (self, stream, varName): - if self.style == "wm": - stream.write (" " + varName + "High = " + varName + ";\n") - stream.write (" " + varName + "Low = " + varName + ";\n") - if self.style == "mma": - stream.write (" " + varName + "Count = 0;\n") - stream.write (" " + varName + "Total = 0;\n") - stream.write (" " + varName + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") - stream.write (" " + varName + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") - - def genPerThreadHiLoStatResets (self, stream, varName, cpptype): - if self.style == "mma": - stream.write (" threadStats->" + varName + "Count = 0;\n") - stream.write (" threadStats->" + varName + "Total = 0;\n") - stream.write (" threadStats->" + varName + "Min = std::numeric_limits<" + cpptype + ">::max();\n") - stream.write (" threadStats->" + varName + "Max = std::numeric_limits<" + cpptype + ">::min();\n") - - def genWrite (self, stream, varName, indent=" "): - if self.style != "mma": - stream.write (indent + self.encode.replace ("@", "buf").replace ("#", varName) + ";\n") - if self.style == "wm": - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "High") + ";\n") - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "Low") + ";\n") - if self.style == "mma": - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "Count") + ";\n") - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "Count ? " + varName + "Min : 0") + ";\n") - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "Max") + ";\n") - stream.write (indent + self.encode.replace ("@", "buf") \ - .replace ("#", varName + "Count ? " + varName + "Total / " + - varName + "Count : 0") + ";\n") - - - def getReadCode (self, varName, bufName): - result = self.decode.replace ("@", bufName).replace ("#", varName) - return result - - def getWriteCode (self, varName, bufName): - result = self.encode.replace ("@", bufName).replace ("#", varName) - return result - -#===================================================================================== -# -#===================================================================================== -class TypeSpec: - def __init__ (self, file): - self.types = {} - dom = parse (file) - document = dom.documentElement - if document.tagName != 'schema-types': - raise ValueError ("Expected 'schema-types' in type file") - - for child in document.childNodes: - if child.nodeType == Node.ELEMENT_NODE: - if child.nodeName == 'type': - stype = SchemaType (child) - self.types[stype.getName ()] = stype - else: - raise ValueError ("Unknown type tag '%s'" % child.nodeName) - - def getType (self, name): - return self.types[name] - - -#===================================================================================== -# -#===================================================================================== -class Type: - def __init__ (self, name, typespec): - self.type = typespec.getType (name) - -#===================================================================================== -# -#===================================================================================== -class SchemaProperty: - def __init__ (self, node, typespec): - self.name = None - self.type = None - self.ref = None - self.access = "RO" - self.isIndex = 0 - self.isParentRef = 0 - self.isGeneralRef = 0 - self.isOptional = 0 - self.unit = None - self.min = None - self.max = None - self.maxLen = None - self.desc = None - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = makeValidCppSymbol(val) - - elif key == 'type': - self.type = Type (val, typespec) - if self.type.type.accessor != 'direct': - raise ValueError ("Class properties must have a type with a direct accessor") - - elif key == 'references': - self.ref = val - - elif key == 'access': - self.access = val - - elif key == 'index': - if val != 'y': - raise ValueError ("Expected 'y' in index attribute") - self.isIndex = 1 - - elif key == 'parentRef': - if val != 'y': - raise ValueError ("Expected 'y' in parentRef attribute") - self.isParentRef = 1 - - elif key == 'isGeneralReference': - if val != 'y': - raise ValueError ("Expected 'y' in isGeneralReference attribute") - self.isGeneralRef = 1 - - elif key == 'optional': - if val != 'y': - raise ValueError ("Expected 'y' in optional attribute") - self.isOptional = 1 - - elif key == 'unit': - self.unit = val - - elif key == 'min': - self.min = val - - elif key == 'max': - self.max = val - - elif key == 'maxlen': - self.maxLen = val - - elif key == 'desc': - self.desc = val - - else: - raise ValueError ("Unknown attribute in property '%s'" % key) - - if self.access == "RC" and self.isOptional == 1: - raise ValueError ("Properties with ReadCreate access must not be optional (%s)" % self.name) - - if self.name == None: - raise ValueError ("Missing 'name' attribute in property") - if self.type == None: - raise ValueError ("Missing 'type' attribute in property") - - def getName (self): - return self.name - - def isConstructorArg (self): - if self.access == "RC" and self.isParentRef == 0: - return 1 - return 0 - - def genDeclaration (self, stream, prefix=" "): - stream.write (prefix + self.type.type.cpp + " " + self.name + ";\n") - - def genFormalParam (self, stream, variables): - stream.write (self.type.type.cpp + " _" + self.name) - - def genAccessor (self, stream): - self.type.type.genAccessor (stream, self.name, "configChanged", self.isOptional == 1) - - def genSchema (self, stream): - stream.write (" ft = FieldTable ();\n") - stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") - stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") - stream.write (" ft.setInt (ACCESS, ACCESS_" + self.access + ");\n") - stream.write (" ft.setInt (INDEX, " + str (self.isIndex) + ");\n") - stream.write (" ft.setInt (OPTIONAL, " + str (self.isOptional) + ");\n") - if self.unit != None: - stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") - if self.min != None: - stream.write (" ft.setInt (MIN, " + self.min + ");\n") - if self.max != None: - stream.write (" ft.setInt (MAX, " + self.max + ");\n") - if self.maxLen != None: - stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") - if self.desc != None: - stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") - stream.write (" buf.put (ft);\n\n") - - def genWrite (self, stream): - indent = " " - if self.isOptional: - stream.write(" if (presenceMask[presenceByte_%s] & presenceMask_%s) {\n" % (self.name, self.name)) - indent = " " - self.type.type.genWrite (stream, self.name, indent) - if self.isOptional: - stream.write(" }\n") - - -#===================================================================================== -# -#===================================================================================== -class SchemaStatistic: - def __init__ (self, node, typespec): - self.name = None - self.type = None - self.unit = None - self.desc = None - self.assign = None - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = makeValidCppSymbol(val) - - elif key == 'type': - self.type = Type (val, typespec) - - elif key == 'unit': - self.unit = val - - elif key == 'desc': - self.desc = val - - elif key == 'assign': - self.assign = val - - else: - raise ValueError ("Unknown attribute in statistic '%s'" % key) - - if self.name == None: - raise ValueError ("Missing 'name' attribute in statistic") - if self.type == None: - raise ValueError ("Missing 'type' attribute in statistic") - - def getName (self): - return self.name - - def genDeclaration (self, stream, prefix=" "): - if self.type.type.style != "mma": - stream.write (prefix + self.type.type.cpp + " " + self.name + ";\n") - if self.type.type.style == 'wm': - stream.write (prefix + self.type.type.cpp + " " + self.name + "High;\n") - stream.write (prefix + self.type.type.cpp + " " + self.name + "Low;\n") - if self.type.type.style == "mma": - stream.write (prefix + self.type.type.cpp + " " + self.name + "Count;\n") - stream.write (prefix + "uint64_t " + self.name + "Total;\n") - stream.write (prefix + self.type.type.cpp + " " + self.name + "Min;\n") - stream.write (prefix + self.type.type.cpp + " " + self.name + "Max;\n") - - def genAccessor (self, stream): - self.type.type.genAccessor (stream, self.name, "instChanged") - - def genHiLoStatResets (self, stream): - self.type.type.genHiLoStatResets (stream, self.name) - - def genPerThreadHiLoStatResets (self, stream): - self.type.type.genPerThreadHiLoStatResets (stream, self.name, self.type.type.cpp) - - def genSchemaText (self, stream, name, desc): - stream.write (" ft = FieldTable ();\n") - stream.write (" ft.setString (NAME, \"" + name + "\");\n") - stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") - if self.unit != None: - stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") - if desc != None: - stream.write (" ft.setString (DESC, \"" + desc + "\");\n") - stream.write (" buf.put (ft);\n\n") - - def genSchema (self, stream): - if self.type.type.style != "mma": - self.genSchemaText (stream, self.name, self.desc) - if self.type.type.style == "wm": - descHigh = self.desc - descLow = self.desc - if self.desc != None: - descHigh = descHigh + " (High)" - descLow = descLow + " (Low)" - self.genSchemaText (stream, self.name + "High", descHigh) - self.genSchemaText (stream, self.name + "Low", descLow) - if self.type.type.style == "mma": - descCount = self.desc - descMin = self.desc - descMax = self.desc - descAverage = self.desc - if self.desc != None: - descCount = descCount + " (Samples)" - descMin = descMin + " (Min)" - descMax = descMax + " (Max)" - descAverage = descAverage + " (Average)" - self.genSchemaText (stream, self.name + "Samples", descCount) - self.genSchemaText (stream, self.name + "Min", descMin) - self.genSchemaText (stream, self.name + "Max", descMax) - self.genSchemaText (stream, self.name + "Average", descAverage) - - def genAssign (self, stream): - if self.assign != None: - if self.type.type.perThread: - prefix = " threadStats->" - else: - prefix = "" - stream.write (" " + prefix + self.name + " = (" + self.type.type.cpp + - ") (" + self.assign + ");\n") - - def genWrite (self, stream): - if self.type.type.perThread: - self.type.type.genWrite (stream, "totals." + self.name) - else: - self.type.type.genWrite (stream, self.name) - - def genInitialize (self, stream, prefix="", indent=" "): - val = self.type.type.init - if self.type.type.style != "mma": - stream.write (indent + prefix + self.name + " = " + val + ";\n") - if self.type.type.style == "wm": - stream.write (indent + prefix + self.name + "High = " + val + ";\n") - stream.write (indent + prefix + self.name + "Low = " + val + ";\n") - if self.type.type.style == "mma": - stream.write (indent + prefix + self.name + "Count = 0;\n") - stream.write (indent + prefix + self.name + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") - stream.write (indent + prefix + self.name + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") - stream.write (indent + prefix + self.name + "Total = 0;\n") - - def genInitializeTotalPerThreadStats (self, stream): - if self.type.type.style == "mma": - stream.write (" totals->" + self.name + "Count = 0;\n") - stream.write (" totals->" + self.name + "Min = std::numeric_limits<" + self.type.type.cpp + ">::max();\n") - stream.write (" totals->" + self.name + "Max = std::numeric_limits<" + self.type.type.cpp + ">::min();\n") - stream.write (" totals->" + self.name + "Total = 0;\n") - else: - stream.write (" totals->" + self.name + " = 0;\n") - - def genAggregatePerThreadStats (self, stream): - if self.type.type.style == "mma": - stream.write (" totals->%sCount += threadStats->%sCount;\n" % (self.name, self.name)) - stream.write (" if (totals->%sMin > threadStats->%sMin)\n" % (self.name, self.name)) - stream.write (" totals->%sMin = threadStats->%sMin;\n" % (self.name, self.name)) - stream.write (" if (totals->%sMax < threadStats->%sMax)\n" % (self.name, self.name)) - stream.write (" totals->%sMax = threadStats->%sMax;\n" % (self.name, self.name)) - stream.write (" totals->%sTotal += threadStats->%sTotal;\n" % (self.name, self.name)) - else: - stream.write (" totals->%s += threadStats->%s;\n" % (self.name, self.name)) - -#===================================================================================== -# -#===================================================================================== -class SchemaArg: - def __init__ (self, node, typespec): - self.name = None - self.type = None - self.unit = None - self.dir = "I" - self.min = None - self.max = None - self.maxLen = None - self.desc = None - self.default = None - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = makeValidCppSymbol(val) - - elif key == 'type': - self.type = Type (val, typespec) - - elif key == 'unit': - self.unit = val - - elif key == 'dir': - self.dir = val.upper () - - elif key == 'min': - self.min = val - - elif key == 'max': - self.max = val - - elif key == 'maxlen': - self.maxLen = val - - elif key == 'desc': - self.desc = val - - elif key == 'default': - self.default = val - - else: - raise ValueError ("Unknown attribute in arg '%s'" % key) - - if self.name == None: - raise ValueError ("Missing 'name' attribute in arg") - if self.type == None: - raise ValueError ("Missing 'type' attribute in arg") - - def getName (self): - return self.name - - def getDir (self): - return self.dir - - def genSchema (self, stream, event=False): - stream.write (" ft = FieldTable ();\n") - stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") - stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") - if (not event): - stream.write (" ft.setString (DIR, \"" + self.dir + "\");\n") - if self.unit != None: - stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") - if not event: - if self.min != None: - stream.write (" ft.setInt (MIN, " + self.min + ");\n") - if self.max != None: - stream.write (" ft.setInt (MAX, " + self.max + ");\n") - if self.maxLen != None: - stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") - if self.default != None: - stream.write (" ft.setString (DEFAULT, \"" + self.default + "\");\n") - if self.desc != None: - stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") - stream.write (" buf.put (ft);\n\n") - - def genFormalParam (self, stream, variables): - stream.write ("%s _%s" % (self.type.type.cpp, self.name)) - -#===================================================================================== -# -#===================================================================================== -class SchemaMethod: - def __init__ (self, parent, node, typespec): - self.parent = parent - self.name = None - self.desc = None - self.args = [] - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = makeValidCppSymbol(val) - - elif key == 'desc': - self.desc = val - - else: - raise ValueError ("Unknown attribute in method '%s'" % key) - - for child in node.childNodes: - if child.nodeType == Node.ELEMENT_NODE: - if child.nodeName == 'arg': - arg = SchemaArg (child, typespec) - self.args.append (arg) - else: - raise ValueError ("Unknown method tag '%s'" % child.nodeName) - - def getName (self): - return self.name - - def getFullName (self): - return capitalize(self.parent.getName()) + self.name[0:1].upper() +\ - self.name[1:] - - def getArgCount (self): - return len (self.args) - - #=================================================================================== - # Code Generation Functions. The names of these functions (minus the leading "gen") - # match the substitution keywords in the template files. - #=================================================================================== - def genNameUpper (self, stream, variables): - stream.write (self.getFullName ().upper ()) - - def genNameCamel (self, stream, variables): - stream.write (self.getFullName ()) - - def genArguments (self, stream, variables): - for arg in self.args: - ctype = arg.type.type.cpp - dirTag = arg.dir.lower() + "_" - stream.write (" " + ctype + " " + dirTag + arg.getName () + ";\n") - - def genSchema (self, stream, variables): - stream.write (" ft = FieldTable ();\n") - stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") - stream.write (" ft.setInt (ARGCOUNT, " + str (len (self.args)) + ");\n") - if self.desc != None: - stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") - stream.write (" buf.put (ft);\n\n") - for arg in self.args: - arg.genSchema (stream) - -#===================================================================================== -# -#===================================================================================== -class SchemaEvent: - def __init__ (self, parent, node, typespec): - self.parent = parent - self.name = None - self.desc = None - self.args = [] - - attrs = node.attributes - for idx in range (attrs.length): - key = attrs.item(idx).nodeName - val = attrs.item(idx).nodeValue - if key == 'name': - self.name = val - - elif key == 'desc': - self.desc = val - - else: - raise ValueError ("Unknown attribute in event '%s'" % key) - - for child in node.childNodes: - if child.nodeType == Node.ELEMENT_NODE: - if child.nodeName == 'arg': - arg = SchemaArg (child, typespec) - self.args.append (arg) - else: - raise ValueError ("Unknown event tag '%s'" % child.nodeName) - - def getName (self): - return self.name - - def getFullName (self): - return capitalize(self.parent.getName()) + capitalize(self.name) - - def getArgCount (self): - return len (self.args) - - def genMethodBody (self, stream, variables, classObject): - stream.write("void ") - classObject.genNameCap(stream, variables) - stream.write("::event_%s(" % self.name) - count = 0 - for arg in self.args: - arg.genFormalParam(stream, variables) - count += 1 - if count < len(self.args): - stream.write(", ") - stream.write(") {\n") - stream.write(" sys::Mutex::ScopedLock mutex(getMutex());\n") - stream.write(" Buffer* buf = startEventLH();\n") - stream.write(" objectId.encode(*buf);\n") - stream.write(" buf->putShortString(packageName);\n") - stream.write(" buf->putShortString(className);\n") - stream.write(" buf->putBin128(md5Sum);\n") - stream.write(" buf->putShortString(\"%s\");\n" % self.name) - for arg in self.args: - stream.write(" %s;\n" % arg.type.type.encode.replace("@", "(*buf)").replace("#", "_" + arg.name)) - stream.write(" finishEventLH(buf);\n") - stream.write("}\n\n") - - def genMethodDecl (self, stream, variables): - stream.write(" void event_%s(" % self.name) - count = 0 - for arg in self.args: - arg.genFormalParam(stream, variables) - count += 1 - if count < len(self.args): - stream.write(", ") - stream.write(");\n") - - def genSchema(self, stream, variables): - stream.write (" ft = FieldTable ();\n") - stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") - stream.write (" ft.setInt (ARGCOUNT, " + str (len (self.args)) + ");\n") - if self.desc != None: - stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") - stream.write (" buf.put (ft);\n\n") - for arg in self.args: - arg.genSchema (stream, True) - - - -class SchemaClass: - def __init__ (self, package, node, typespec, fragments, options): - self.packageName = package - self.properties = [] - self.statistics = [] - self.methods = [] - self.events = [] - self.options = options - self.md5Sum = md5.new () - - self.hash (node) - - attrs = node.attributes - self.name = makeValidCppSymbol(attrs['name'].nodeValue) - - children = node.childNodes - for child in children: - if child.nodeType == Node.ELEMENT_NODE: - if child.nodeName == 'property': - sub = SchemaProperty (child, typespec) - self.properties.append (sub) - - elif child.nodeName == 'statistic': - sub = SchemaStatistic (child, typespec) - self.statistics.append (sub) - - elif child.nodeName == 'method': - sub = SchemaMethod (self, child, typespec) - self.methods.append (sub) - - elif child.nodeName == 'event': - sub = SchemaEvent (self, child, typespec) - self.events.append (sub) - - elif child.nodeName == 'group': - self.expandFragment (child, fragments) - - else: - raise ValueError ("Unknown class tag '%s'" % child.nodeName) - - # Adjust the 'assign' attributes for each statistic - for stat in self.statistics: - if stat.assign != None and stat.type.type.perThread: - stat.assign = self.adjust (stat.assign, self.statistics) - - def adjust (self, text, statistics): - result = text - start = 0 - while True: - next = None - for stat in statistics: - pos = result.find (stat.name, start) - if pos != -1 and (next == None or pos < next[0]): - next = (pos, stat.name) - if next == None: - return result - pos = next[0] - result = result[0:pos] + "threadStats->" + result[pos:] - start = pos + 9 + len(next[1]) - - def hash (self, node): - attrs = node.attributes - self.md5Sum.update (node.nodeName) - - for idx in range (attrs.length): - self.md5Sum.update (attrs.item(idx).nodeName) - self.md5Sum.update (attrs.item(idx).nodeValue) - - for child in node.childNodes: - if child.nodeType == Node.ELEMENT_NODE: - self.hash (child) - - def expandFragment (self, node, fragments): - attrs = node.attributes - name = attrs['name'].nodeValue - for fragment in fragments: - if fragment.name == name: - self.md5Sum.update (fragment.md5Sum.digest()) - for config in fragment.properties: - self.properties.append (config) - for inst in fragment.statistics: - self.statistics.append (inst) - for method in fragment.methods: - self.methods.append (method) - for event in fragment.events: - self.events.append (event) - return - raise ValueError ("Undefined group '%s'" % name) - - def getName (self): - return self.name - - def getNameCap (self): - return capitalize(self.name) - - def getMethods (self): - return self.methods - - def getEvents (self): - return self.events - - def getPackageNameCap (self): - return capitalize(self.packageName) - - #=================================================================================== - # Code Generation Functions. The names of these functions (minus the leading "gen") - # match the substitution keywords in the template files. - #=================================================================================== - def testExistOptionals (self, variables): - for prop in self.properties: - if prop.isOptional == 1: - return True - return False - - def testExistPerThreadStats (self, variables): - for inst in self.statistics: - if inst.type.type.perThread: - return True - return False - - def testExistPerThreadAssign (self, variables): - for inst in self.statistics: - if inst.type.type.perThread and inst.assign != None: - return True - return False - - def testExistPerThreadResets (self, variables): - for inst in self.statistics: - if inst.type.type.perThread and inst.type.type.style == "mma": - return True - return False - - def testNoStatistics (self, variables): - return len (self.statistics) == 0 - - def genAccessorMethods (self, stream, variables): - for config in self.properties: - if config.access != "RC": - config.genAccessor (stream) - for inst in self.statistics: - if inst.assign == None: - inst.genAccessor (stream) - - def genConfigCount (self, stream, variables): - stream.write ("%d" % len (self.properties)) - - def genConfigDeclarations (self, stream, variables): - for element in self.properties: - element.genDeclaration (stream) - - def genConstructorArgs (self, stream, variables): - # Constructor args are config elements with read-create access - result = "" - for element in self.properties: - if element.isConstructorArg (): - stream.write (", ") - element.genFormalParam (stream, variables) - - def genConstructorInits (self, stream, variables): - for element in self.properties: - if element.isConstructorArg (): - stream.write ("," + element.getName () + "(_" + element.getName () + ")") - - def genDoMethodArgs (self, stream, variables): - methodCount = 0 - inArgCount = 0 - for method in self.methods: - methodCount = methodCount + 1 - for arg in method.args: - if arg.getDir () == "I" or arg.getDir () == "IO": - inArgCount = inArgCount + 1 - - if methodCount == 0: - stream.write ("string, Buffer&, Buffer& outBuf") - else: - if inArgCount == 0: - stream.write ("string methodName, Buffer&, Buffer& outBuf") - else: - stream.write ("string methodName, Buffer& inBuf, Buffer& outBuf") - - def genEventCount (self, stream, variables): - stream.write ("%d" % len (self.events)) - - def genEventMethodBodies (self, stream, variables): - for event in self.events: - event.genMethodBody (stream, variables, self) - - def genEventMethodDecls (self, stream, variables): - for event in self.events: - event.genMethodDecl (stream, variables) - - def genEventSchema (self, stream, variables): - for event in self.events: - event.genSchema (stream, variables) - - def genHiLoStatResets (self, stream, variables): - for inst in self.statistics: - if not inst.type.type.perThread: - inst.genHiLoStatResets (stream) - - def genPerThreadHiLoStatResets (self, stream, variables): - for inst in self.statistics: - if inst.type.type.perThread: - inst.genPerThreadHiLoStatResets (stream) - - def genInitializeElements (self, stream, variables): - for inst in self.statistics: - if not inst.type.type.perThread: - inst.genInitialize (stream) - - def genInitializePerThreadElements (self, stream, variables): - for inst in self.statistics: - if inst.type.type.perThread: - inst.genInitialize (stream, "threadStats->", " ") - - def genInitializeTotalPerThreadStats (self, stream, variables): - for inst in self.statistics: - if inst.type.type.perThread: - inst.genInitializeTotalPerThreadStats (stream) - - def genAggregatePerThreadStats (self, stream, variables): - for inst in self.statistics: - if inst.type.type.perThread: - inst.genAggregatePerThreadStats (stream) - - def genInstCount (self, stream, variables): - count = 0 - for inst in self.statistics: - count = count + 1 - if inst.type.type.style == "wm": - count = count + 2 - if inst.type.type.style == "mma": - count = count + 3 - stream.write ("%d" % count) - - def genInstDeclarations (self, stream, variables): - for element in self.statistics: - if not element.type.type.perThread: - element.genDeclaration (stream) - - def genPerThreadDeclarations (self, stream, variables): - for element in self.statistics: - if element.type.type.perThread: - element.genDeclaration (stream, " ") - - def genMethodArgIncludes (self, stream, variables): - for method in self.methods: - if method.getArgCount () > 0: - stream.write ("#include \"Args" + method.getFullName () + ".h\"\n") - - def genMethodCount (self, stream, variables): - stream.write ("%d" % len (self.methods)) - - def genMethodHandlers (self, stream, variables): - for method in self.methods: - stream.write ("\n if (methodName == \"" + method.getName () + "\") {\n") - if method.getArgCount () == 0: - stream.write (" ArgsNone ioArgs;\n") - else: - stream.write (" Args" + method.getFullName () + " ioArgs;\n") - for arg in method.args: - if arg.getDir () == "I" or arg.getDir () == "IO": - stream.write (" " +\ - arg.type.type.getReadCode ("ioArgs." +\ - arg.dir.lower () + "_" +\ - arg.name, "inBuf") + ";\n") - - stream.write (" status = coreObject->ManagementMethod (METHOD_" +\ - method.getName().upper() + ", ioArgs);\n") - stream.write (" outBuf.putLong (status);\n") - stream.write (" outBuf.putShortString (Manageable::StatusText (status));\n") - for arg in method.args: - if arg.getDir () == "O" or arg.getDir () == "IO": - stream.write (" " +\ - arg.type.type.getWriteCode ("ioArgs." +\ - arg.dir.lower () + "_" +\ - arg.name, "outBuf") + ";\n") - stream.write (" return;\n }\n") - - def genPresenceMaskBytes (self, stream, variables): - count = 0 - for prop in self.properties: - if prop.isOptional == 1: - count += 1 - if count == 0: - stream.write("0") - else: - stream.write (str(((count - 1) / 8) + 1)) - - def genPresenceMaskConstants (self, stream, variables): - count = 0 - for prop in self.properties: - if prop.isOptional == 1: - stream.write(" static const uint8_t presenceByte_%s = %d;\n" % (prop.name, count / 8)) - stream.write(" static const uint8_t presenceMask_%s = %d;\n" % (prop.name, 1 << (count % 8))) - count += 1 - - def genPropertySchema (self, stream, variables): - for prop in self.properties: - prop.genSchema (stream) - - def genSetGeneralReferenceDeclaration (self, stream, variables): - for prop in self.properties: - if prop.isGeneralRef: - stream.write ("void setReference(ObjectId objectId) { " + prop.name + " = objectId; }\n") - - def genStatisticSchema (self, stream, variables): - for stat in self.statistics: - stat.genSchema (stream) - - def genMethodIdDeclarations (self, stream, variables): - number = 1 - for method in self.methods: - stream.write (" static const uint32_t METHOD_" + method.getName().upper() +\ - " = %d;\n" % number) - number = number + 1 - - def genMethodSchema (self, stream, variables): - for method in self.methods: - method.genSchema (stream, variables) - - def genNameCap (self, stream, variables): - stream.write (capitalize(self.name)) - - def genNameLower (self, stream, variables): - stream.write (self.name.lower ()) - - def genNamePackageCap (self, stream, variables): - stream.write (self.getPackageNameCap ()) - - def genNamePackageLower (self, stream, variables): - stream.write (self.packageName.lower ()) - - def genNameUpper (self, stream, variables): - stream.write (self.name.upper ()) - - def genParentArg (self, stream, variables): - for config in self.properties: - if config.isParentRef == 1: - stream.write (", Manageable* _parent") - return - - def genParentRefAssignment (self, stream, variables): - for config in self.properties: - if config.isParentRef == 1: - stream.write (config.getName () + \ - " = _parent->GetManagementObject ()->getObjectId ();") - return - - def genSchemaMD5 (self, stream, variables): - sum = self.md5Sum.digest () - for idx in range (len (sum)): - if idx != 0: - stream.write (",") - stream.write (hex (ord (sum[idx]))) - - def genAssign (self, stream, variables): - for inst in self.statistics: - if not inst.type.type.perThread: - inst.genAssign (stream) - - def genPerThreadAssign (self, stream, variables): - for inst in self.statistics: - if inst.type.type.perThread: - inst.genAssign (stream) - - def genWriteProperties (self, stream, variables): - for prop in self.properties: - prop.genWrite (stream) - - def genWriteStatistics (self, stream, variables): - for stat in self.statistics: - stat.genWrite (stream) - - - -class PackageSchema: - def __init__ (self, typefile, schemafile, options): - - self.classes = [] - self.fragments = [] - self.typespec = TypeSpec (typefile) - - dom = parse (schemafile) - document = dom.documentElement - if document.tagName != 'schema': - raise ValueError ("Expected 'schema' node") - attrs = document.attributes - self.packageName = makeValidCppSymbol(attrs['package'].nodeValue) - - children = document.childNodes - for child in children: - if child.nodeType == Node.ELEMENT_NODE: - if child.nodeName == 'class': - cls = SchemaClass (self.packageName, child, self.typespec, - self.fragments, options) - self.classes.append (cls) - - elif child.nodeName == 'group': - cls = SchemaClass (self.packageName, child, self.typespec, - self.fragments, options) - self.fragments.append (cls) - - else: - raise ValueError ("Unknown schema tag '%s'" % child.nodeName) - - def getPackageName (self): - return self.packageName - - def getPackageNameCap (self): - return capitalize(self.packageName) - - def getClasses (self): - return self.classes - - def genPackageNameUpper (self, stream, variables): - stream.write (self.packageName.upper ()) - - def genPackageNameCap (self, stream, variables): - stream.write (self.getPackageNameCap ()) - - def genClassIncludes (self, stream, variables): - for _class in self.classes: - stream.write ("#include \"") - _class.genNameCap (stream, variables) - stream.write (".h\"\n") - - def genClassRegisters (self, stream, variables): - for _class in self.classes: - stream.write (" ") - _class.genNameCap (stream, variables) - stream.write ("::registerClass(agent);\n") - - -#===================================================================================== -# Utility Functions -#===================================================================================== - -# Create a valid C++ symbol from the input string so that it can be -# used in generated C++ source. For instance, change "qpid.mgmt" to -# "qpidMgmt". -# -# Input: Raw string (str) to process -# Output: String (str) suitable for use as a C++ symbol -# -# Limitations: Currently, only strips periods ('.') from strings, -# eventually should strip :'s and ,'s and ''s, oh my! -def makeValidCppSymbol(input): - output = str() - capitalize = False - - for char in input: - skip = False - - if char == ".": - capitalize = True - skip = True - - if not skip: - output += capitalize and char.upper() or char - - capitalize = False - - return output - -# Capitalize a string by /only/ forcing the first character to be -# uppercase. The rest of the string is left alone. This is different -# from str.capitalize(), which forces the first character to uppercase -# and the rest to lowercase. -# -# Input: A string (str) to capitalize -# Output: A string (str) with the first character as uppercase -def capitalize(input): - return input[0].upper() + input[1:] diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 3f46874b20..25c27549c3 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -28,10 +28,8 @@ $(rgen_generator): # Management generator. mgen_dir=$(top_srcdir)/managementgen -mgen_cmd=$(mgen_dir)/main.py -m $(srcdir)/managementgen.mk \ - $(top_srcdir)/../specs/management-schema.xml \ - $(top_srcdir)/../specs/management-types.xml \ - $(mgen_dir)/templates gen/qpid/management +mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk \ + $(top_srcdir)/../specs/management-schema.xml gen/qpid/management $(srcdir)/managementgen.mk $(mgen_broker_cpp) $(dist_qpid_management_HEADERS): mgen.timestamp mgen.timestamp: $(mgen_generator) @@ -100,6 +98,7 @@ lib_LTLIBRARIES = libqpidcommon.la libqpidbroker.la libqpidclient.la include cluster.mk include acl.mk +include qmf.mk # The logger library uses boost::date_time to format time. # We have to disable the unused parameters warning to get around @@ -325,7 +324,6 @@ libqpidclient_la_LIBADD = libqpidcommon.la -luuid libqpidclient_la_SOURCES = \ $(rgen_client_srcs) \ - qpid/agent/ManagementAgentImpl.cpp \ qpid/client/AckPolicy.cpp \ qpid/client/Bounds.cpp \ qpid/client/ConnectionImpl.cpp \ diff --git a/cpp/src/qmf.mk b/cpp/src/qmf.mk new file mode 100644 index 0000000000..3b60be1b96 --- /dev/null +++ b/cpp/src/qmf.mk @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# 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 License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# qmf agent library makefile fragment, to be included in Makefile.am +# +lib_LTLIBRARIES += libqmfagent.la + +libqmfagent_la_SOURCES = \ + qpid/agent/ManagementAgent.h \ + qpid/agent/ManagementAgentImpl.cpp \ + qpid/agent/ManagementAgentImpl.h + +libqmfagent_la_LIBADD = libqpidclient.la + -- cgit v1.2.1