diff options
author | David Wagner <david.wagner@intel.com> | 2015-01-08 17:11:44 +0100 |
---|---|---|
committer | David Wagner <david.wagner@intel.com> | 2015-01-28 20:02:38 +0100 |
commit | 7d996811c0e80b30a40a5add1f01f0314f2ebef4 (patch) | |
tree | d2ecef1672edc7ba183fe902f2b778b32a171cbc /tools | |
parent | fa09759eddd515fb2f83810ba76f9a6f8878093f (diff) | |
download | external_parameter-framework-7d996811c0e80b30a40a5add1f01f0314f2ebef4.zip external_parameter-framework-7d996811c0e80b30a40a5add1f01f0314f2ebef4.tar.gz external_parameter-framework-7d996811c0e80b30a40a5add1f01f0314f2ebef4.tar.bz2 |
xmlGenerator: Refactor PFWScriptGenerator
The existing code is split in two parts:
- "EDD"-files parsing;
- parameter-framework remote commands generation
The first part is mostly kept as-is but the file is renamed to "EddParser.py";
the second part is refactored and an object presenting a "Translator" intreface
is passed to the parsed objects in order to generate the remote
parameter-framework commands. Later, another class implementing the Translator
interface will be implemented using the python bindings.
Some dead code is removed in the process.
Change-Id: I9725600ce34f36742c7e27ea7aee53892dd799b0
Signed-off-by: David Wagner <david.wagner@intel.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/xmlGenerator/Android.mk | 19 | ||||
-rw-r--r-- | tools/xmlGenerator/CMakeLists.txt | 3 | ||||
-rwxr-xr-x | tools/xmlGenerator/EddParser.py | 890 | ||||
-rwxr-xr-x | tools/xmlGenerator/PFWScriptGenerator.py | 1269 | ||||
-rw-r--r-- | tools/xmlGenerator/PfwBaseTranslator.py | 177 | ||||
-rwxr-xr-x | tools/xmlGenerator/hostDomainGenerator.sh | 2 | ||||
-rwxr-xr-x | tools/xmlGenerator/lightRoutingUpdate.sh | 2 | ||||
-rwxr-xr-x | tools/xmlGenerator/updateRoutageDomains.sh | 2 |
8 files changed, 1190 insertions, 1174 deletions
diff --git a/tools/xmlGenerator/Android.mk b/tools/xmlGenerator/Android.mk index ebbc9e3..f98077e 100644 --- a/tools/xmlGenerator/Android.mk +++ b/tools/xmlGenerator/Android.mk @@ -33,11 +33,30 @@ LOCAL_PATH := $(call my-dir) # Scripts are not compiled so the prebuild mechanism is used to export them. include $(CLEAR_VARS) +LOCAL_MODULE := EddParser.py +LOCAL_MODULE_OWNER := intel +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := PfwBaseTranslator.py +LOCAL_MODULE_OWNER := intel +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) LOCAL_MODULE := PFWScriptGenerator.py LOCAL_MODULE_OWNER := intel LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_IS_HOST_MODULE := true +LOCAL_REQUIRED_MODULES := \ + PfwBaseTranslator.py \ + EddParser.py include $(BUILD_PREBUILT) include $(CLEAR_VARS) diff --git a/tools/xmlGenerator/CMakeLists.txt b/tools/xmlGenerator/CMakeLists.txt index 57928bc..5dba835 100644 --- a/tools/xmlGenerator/CMakeLists.txt +++ b/tools/xmlGenerator/CMakeLists.txt @@ -28,9 +28,12 @@ install(PROGRAMS domainGenerator.sh + domainGenerator.py hostConfig.py hostDomainGenerator.sh lightRoutingUpdate.sh + PfwBaseTranslator.py + EddParser.py PFWScriptGenerator.py portAllocator.py updateRoutageDomains.sh diff --git a/tools/xmlGenerator/EddParser.py b/tools/xmlGenerator/EddParser.py new file mode 100755 index 0000000..97a59a7 --- /dev/null +++ b/tools/xmlGenerator/EddParser.py @@ -0,0 +1,890 @@ +#!/usr/bin/python2 +# -*-coding:utf-8 -* + +# Copyright (c) 2011-2014, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +import re +import sys +import copy +from itertools import izip +from itertools import imap + +# ===================================================================== +""" Context classes, used during propagation and the "to PFW script" step """ +# ===================================================================== + +class PropagationContextItem(list) : + """Handle an item during the propagation step""" + def __copy__(self): + """C.__copy__() -> a shallow copy of C""" + return self.__class__(self) + +class PropagationContextElement(PropagationContextItem) : + """Handle an Element during the propagation step""" + def getElementsFromName(self, name): + matchingElements = [] + for element in self : + if element.getName() == name : + matchingElements.append(element) + return matchingElements + + +class PropagationContextOption(PropagationContextItem) : + """Handle an Option during the propagation step""" + def getOptionItems (self, itemName): + items = [] + for options in self : + items.append(options.getOption(itemName)) + return items + + +class PropagationContext() : + """Handle the context during the propagation step""" + def __init__(self, propagationContext=None) : + + if propagationContext == None : + self._context = { + "DomainOptions" : PropagationContextOption() , + "Configurations" : PropagationContextElement() , + "ConfigurationOptions" : PropagationContextOption() , + "Rules" : PropagationContextElement() , + "PathOptions" : PropagationContextOption() , + } + else : + self._context = propagationContext + + def copy(self): + """return a copy of the context""" + contextCopy = self._context.copy() + + for key in iter(self._context) : + contextCopy[key] = contextCopy[key].__copy__() + + return self.__class__(contextCopy) + + def getDomainOptions (self): + return self._context["DomainOptions"] + + def getConfigurations (self): + return self._context["Configurations"] + + def getConfigurationOptions (self): + return self._context["ConfigurationOptions"] + + def getRules (self): + return self._context["Rules"] + + def getPathOptions (self): + return self._context["PathOptions"] + + +# ===================================================== +"""Element option container""" +# ===================================================== + +class Options () : + """handle element options""" + def __init__(self, options=[], optionNames=[]) : + self.options = dict(izip(optionNames, options)) + # print(options,optionNames,self.options) + + + def __str__(self) : + ops2str = [] + for name, argument in self.options.items() : + ops2str.append(str(name) + "=\"" + str(argument) + "\"") + + return " ".join(ops2str) + + def getOption(self, name): + """get option by its name, if it does not exist return empty string""" + return self.options.get(name, "") + + def setOption(self, name, newOption): + """set option by its name""" + self.options[name] = newOption + + def copy (self): + """D.copy() -> a shallow copy of D""" + copy = Options() + copy.options = self.options.copy() + return copy + +# ==================================================== +"""Definition of all element class""" +# ==================================================== + +class Element(object): + """ implement a basic element + + It is the class base for all other elements as Domain, Configuration...""" + tag = "unknown" + optionNames = ["Name"] + childWhiteList = [] + optionDelimiter = " " + + def __init__(self, line=None) : + + if line == None : + self.option = Options([], self.optionNames) + else : + self.option = self.optionFromLine(line) + + self.children = [] + + def optionFromLine(self, line) : + # get ride of spaces + line = line.strip() + + options = self.extractOptions(line) + + return Options(options, self.optionNames) + + def extractOptions(self, line) : + """return the line splited by the optionDelimiter atribute + + Option list length is less or equal to the optionNames list length + """ + options = line.split(self.optionDelimiter, len(self.optionNames) - 1) + + # get ride of leftover spaces + optionsStrip = list(imap(str.strip, options)) + + return optionsStrip + + def addChild(self, child, append=True) : + """ A.addChid(B) -> add B to A child list if B class name is in A white List""" + try: + # Will raise an exception if this child is not in the white list + self.childWhiteList.index(child.__class__.__name__) + # If no exception was raised, add child to child list + + if append : + self.children.append(child) + else : + self.children.insert(0, child) + + except ValueError: + # the child class is not in the white list + raise ChildNotPermitedError("", self, child) + + def addChildren(self, children, append=True) : + """Add a list of child""" + if append: + # Add children at the end of the child list + self.children.extend(children) + else: + # Add children at the begining of the child list + self.children = children + self.children + + def childrenToString(self, prefix=""): + """return raw printed children """ + body = "" + for child in self.children : + body = body + child.__str__(prefix) + + return body + + def __str__(self, prefix="") : + """return raw printed element""" + selfToString = prefix + " " + self.tag + " " + str(self.option) + return selfToString + "\n" + self.childrenToString(prefix + "\t") + + def extractChildrenByClass(self, classTypeList) : + """return all children whose class is in the list argument + + return a list of all children whose class in the list "classTypeList" (second arguments)""" + selectedChildren = [] + + for child in self.children : + for classtype in classTypeList : + if child.__class__ == classtype : + selectedChildren.append(child) + break + return selectedChildren + + def propagate (self, context=PropagationContext()): + """call the propagate method of all children""" + for child in self.children : + child.propagate(context) + + def getName(self): + """return name option value. If none return "" """ + return self.option.getOption("Name") + + def setName(self, name): + self.option.setOption("Name", name) + + def translate(self, translator): + for child in self.children: + child.translate(translator) + +# ---------------------------------------------------------- + +class ElementWithTag (Element): + """Element of this class are declared with a tag => line == "tag: .*" """ + def extractOptions(self, line) : + lineWithoutTag = line.split(":", 1)[-1].strip() + options = super(ElementWithTag, self).extractOptions(lineWithoutTag) + return options + +# ---------------------------------------------------------- + +class ElementWithInheritance(Element): + def propagate (self, context=PropagationContext) : + """propagate some proprieties to children""" + + # copy the context so that everything that hapend next will only affect + # children + contextCopy = context.copy() + + # check for inheritance + self.Inheritance(contextCopy) + + # call the propagate method of all children + super(ElementWithInheritance, self).propagate(contextCopy) + + +class ElementWithRuleInheritance(ElementWithInheritance): + """class that will give to its children its rules""" + def ruleInheritance(self, context): + """Add its rules to the context and get context rules""" + + # extract all children rule and operator + childRules = self.extractChildrenByClass([Operator, Rule]) + + # get context rules + contextRules = context.getRules() + + # adopt rules of the beginning of the context + self.addChildren(contextRules, append=False) + + # add previously extract rules to the context + contextRules += childRules + + +# ---------------------------------------------------------- + +class EmptyLine (Element) : + """This class represents an empty line. + + Will raise "EmptyLineWarning" exception at instanciation.""" + + tag = "emptyLine" + match = re.compile(r"[ \t]*\n?$").match + def __init__ (self, line): + raise EmptyLineWarning(line) + +# ---------------------------------------------------------- + +class Commentary(Element): + """This class represents a commentary. + + Will raise "CommentWarning" exception at instanciation.""" + + tag = "commentary" + optionNames = ["comment"] + match = re.compile(r"#").match + def __init__ (self, line): + raise CommentWarning(line) + +# ---------------------------------------------------------- + +class Path (ElementWithInheritance) : + """class implementing the "path = value" concept""" + tag = "path" + optionNames = ["Name", "value"] + match = re.compile(r".+=").match + optionDelimiter = "=" + + def translate(self, translator): + translator.setParameter(self.getName(), self.option.getOption("value")) + + def Inheritance (self, context) : + """check for path name inheritance""" + self.OptionsInheritance(context) + + def OptionsInheritance (self, context) : + """make configuration name inheritance """ + + context.getPathOptions().append(self.option.copy()) + self.setName("/".join(context.getPathOptions().getOptionItems("Name"))) + + +class GroupPath (Path, ElementWithTag) : + tag = "component" + match = re.compile(tag + r" *:").match + optionNames = ["Name"] + childWhiteList = ["Path", "GroupPath"] + + def getPathNames (self) : + """Return the list of all path child name""" + + pathNames = [] + + paths = self.extractChildrenByClass([Path]) + for path in paths : + pathNames.append(path.getName()) + + groupPaths = self.extractChildrenByClass([GroupPath]) + for groupPath in groupPaths : + pathNames += groupPath.getPathNames() + + return pathNames + + def translate(self, translator): + for child in self.extractChildrenByClass([Path, GroupPath]): + child.translate(translator) + +# ---------------------------------------------------------- + +class Rule (Element) : + """class implementing the rule concept + + A rule is composed of a criterion, a rule type and an criterion state. + It should not have any child and is propagated to all configuration in parent descendants. + """ + + tag = "rule" + optionNames = ["criterion", "type", "element"] + match = re.compile(r"[a-zA-Z0-9_.]+ +(Is|IsNot|Includes|Excludes) +[a-zA-Z0-9_.]+").match + childWhiteList = [] + + def PFWSyntax (self, prefix=""): + + script = prefix + \ + self.option.getOption("criterion") + " " + \ + self.option.getOption("type") + " " + \ + self.option.getOption("element") + + return script + + +class Operator (Rule) : + """class implementing the operator concept + + An operator contains rules and other operators + It is as rules propagated to all configuration children in parent descendants. + It should only have the name ANY or ALL to be understood by PFW. + """ + + tag = "operator" + optionNames = ["Name"] + match = re.compile(r"ANY|ALL").match + childWhiteList = ["Rule", "Operator"] + + syntax = { "ANY" : "Any" , "ALL" : "All"} + + def PFWSyntax (self, prefix=""): + """ return a pfw rule (ex : "Any{criterion1 is state1}") generated from "self" and its children options""" + script = "" + + script += prefix + \ + self.syntax[self.getName()] + "{ " + + rules = self.extractChildrenByClass([Rule, Operator]) + + PFWRules = [] + for rule in rules : + PFWRules.append(rule.PFWSyntax(prefix + " ")) + + script += (" , ").join(PFWRules) + + script += prefix + " }" + + return script + +# ---------------------------------------------------------- + +class Configuration (ElementWithRuleInheritance, ElementWithTag) : + tag = "configuration" + optionNames = ["Name"] + match = re.compile(r"conf *:").match + childWhiteList = ["Rule", "Operator", "Path", "GroupPath"] + + def composition (self, context): + """make all needed composition + + Composition is the fact that group configuration with the same name defined + in a parent will give their rule children to this configuration + """ + + name = self.getName() + sameNameConf = context.getConfigurations().getElementsFromName(name) + + sameNameConf.reverse() + + for configuration in sameNameConf : + # add same name configuration rule children to self child list + self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False) + + + def propagate (self, context=PropagationContext) : + """propagate proprieties to children + + make needed compositions, join ancestor name to its name, + and add rules previously defined rules""" + + # make all needed composition + self.composition(context) + + super(Configuration, self).propagate(context) + + def Inheritance (self, context) : + """make configuration name and rule inheritance""" + # check for configuration name inheritance + self.OptionsInheritance(context) + + # check for rule inheritance + self.ruleInheritance(context) + + def OptionsInheritance (self, context) : + """make configuration name inheritance """ + + context.getConfigurationOptions().append(self.option.copy()) + self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name"))) + + + def getRootPath (self) : + + paths = self.extractChildrenByClass([Path, GroupPath]) + + rootPath = GroupPath() + rootPath.addChildren(paths) + + return rootPath + + def getConfigurableElements (self) : + """return all path name defined in this configuration""" + + return self.getRootPath().getPathNames() + + def getRuleString(self): + """Output this configuration's rule as a string""" + + # Create a rootRule + ruleChildren = self.extractChildrenByClass([Rule, Operator]) + + # Do not create a root rule if there is only one fist level Operator rule + if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator : + ruleroot = ruleChildren[0] + + else : + ruleroot = Operator() + ruleroot.setName("ALL") + ruleroot.addChildren(ruleChildren) + + return ruleroot.PFWSyntax() + + def translate(self, translator): + translator.createConfiguration(self.getName()) + translator.setRule(self.getRuleString()) + + paths = self.extractChildrenByClass([Path, GroupPath]) + translator.setElementSequence(self.getConfigurableElements()) + for path in paths: + path.translate(translator) + + def copy (self) : + """return a shallow copy of the configuration""" + + # create configuration or subclass copy + confCopy = self.__class__() + + # add children + confCopy.children = list(self.children) + + # add option + confCopy.option = self.option.copy() + + return confCopy + +class GroupConfiguration (Configuration) : + tag = "GroupConfiguration" + optionNames = ["Name"] + match = re.compile(r"(supConf|confGroup|confType) *:").match + childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"] + + def composition (self, context) : + """add itself in context for configuration composition + + Composition is the fact that group configuration with the same name defined + in a parent will give their rule children to this configuration + """ + + # copyItself + selfCopy = self.copy() + + # make all needed composition + super(GroupConfiguration, self).composition(context) + + # add the copy in context for futur configuration composition + context.getConfigurations().append(selfCopy) + + + def getConfigurableElements (self) : + """return a list. Each elements consist of a list of configurable element of a configuration + + return a list consisting of all configurable elements for each configuration. + These configurable elements are organized in a list""" + configurableElements = [] + + configurations = self.extractChildrenByClass([Configuration]) + for configuration in configurations : + configurableElements.append(configuration.getConfigurableElements()) + + groudeConfigurations = self.extractChildrenByClass([GroupConfiguration]) + for groudeConfiguration in groudeConfigurations : + configurableElements += groudeConfiguration.getConfigurableElements() + + return configurableElements + + def translate(self, translator): + for child in self.extractChildrenByClass([Configuration, GroupConfiguration]): + child.translate(translator) + +# ---------------------------------------------------------- + +class Domain (ElementWithRuleInheritance, ElementWithTag) : + tag = "domain" + sequenceAwareKeyword = "sequenceAware" + + match = re.compile(r"domain *:").match + optionNames = ["Name", sequenceAwareKeyword] + childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"] + + def propagate (self, context=PropagationContext) : + """ propagate name, sequenceAwareness and rule to children""" + + # call the propagate method of all children + super(Domain, self).propagate(context) + + self.checkConfigurableElementUnicity() + + def Inheritance (self, context) : + """check for domain name, sequence awarness and rules inheritance""" + # check for domain name and sequence awarness inheritance + self.OptionsInheritance(context) + + # check for rule inheritance + self.ruleInheritance(context) + + def OptionsInheritance(self, context) : + """ make domain name and sequence awareness inheritance + + join to the domain name all domain names defined in context and + if any domain in context is sequence aware, set sequenceAwareness to True""" + + # add domain options to context + context.getDomainOptions().append(self.option.copy()) + + # set name to the junction of all domain name in context + self.setName(".".join(context.getDomainOptions().getOptionItems("Name"))) + + # get sequenceAwareness of all domains in context + sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword) + # or operation on all booleans in sequenceAwareList + sequenceAwareness = False + for sequenceAware in sequenceAwareList : + sequenceAwareness = sequenceAwareness or sequenceAware + # current domain sequenceAwareness = sequenceAwareness + self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness) + + + def extractOptions(self, line) : + """Extract options from the definition line""" + options = super(Domain, self).extractOptions(line) + + sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword) + + # translate the keyword self.sequenceAwareKeyword if specified to boolean True, + # to False otherwise + try : + if options[sequenceAwareIndex] == self.sequenceAwareKeyword : + options[sequenceAwareIndex] = True + else: + options[sequenceAwareIndex] = False + except IndexError : + options = options + [None] * (sequenceAwareIndex - len(options)) + [False] + return options + + def getRootConfiguration (self) : + """return the root configuration group""" + configurations = self.extractChildrenByClass([Configuration, GroupConfiguration]) + + configurationRoot = GroupConfiguration() + + configurationRoot.addChildren(configurations) + + return configurationRoot + + # TODO: don't do that in the parser, let the PFW tell you that + def checkConfigurableElementUnicity (self): + """ check that all configurable elements defined in child configuration are the sames""" + + # get a list. Each elements of is the configurable element list of a configuration + configurableElementsList = self.getRootConfiguration().getConfigurableElements() + + # if at least two configurations in the domain + if len(configurableElementsList) > 1 : + + # get first configuration configurable element list sort + configurableElementsList0 = list(configurableElementsList[0]) + configurableElementsList0.sort() + + for configurableElements in configurableElementsList : + # sort current configurable element list + auxConfigurableElements = list(configurableElements) + auxConfigurableElements.sort() + + if auxConfigurableElements != configurableElementsList0 : + # if different, 2 configurations those not have the same configurable element list + # => one or more configurable element is missing in one of the 2 configuration + raise UndefinedParameter(self.getName()) + + + def translate(self, translator): + sequence_aware = self.option.getOption(self.sequenceAwareKeyword) + translator.createDomain(self.getName(), sequence_aware) + + configurations = self.getRootConfiguration() + configurableElementsList = configurations.getConfigurableElements() + + # add configurable elements + if len(configurableElementsList) != 0 : + for configurableElement in configurableElementsList[0] : + translator.addElement(configurableElement) + + configurations.translate(translator) + +class GroupDomain (Domain) : + tag = "groupDomain" + match = re.compile(r"(supDomain|domainGroup) *:").match + childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"] + + def translate(self, translator): + for child in self.extractChildrenByClass([Domain, GroupDomain]): + child.translate(translator) + +# ---------------------------------------------------------- + +class Root(Element): + tag = "root" + childWhiteList = ["Domain", "GroupDomain"] + + +# =========================================== +""" Syntax error Exceptions""" +# =========================================== + +class MySyntaxProblems(SyntaxError) : + comment = "syntax error in %(line)s " + + def __init__(self, line=None, num=None): + self.setLine(line, num) + + def __str__(self): + + if self.line : + self.comment = self.comment % {"line" : repr(self.line)} + if self.num : + self.comment = "Line " + str(self.num) + ", " + self.comment + return self.comment + + def setLine (self, line, num): + self.line = str(line) + self.num = num + + +# --------------------------------------------------------- + +class MyPropagationError(MySyntaxProblems) : + """ Syntax error Exceptions used in the propagation step""" + pass + +class UndefinedParameter(MyPropagationError) : + comment = "Configurations in domain '%(domainName)s' do not all set the same parameters " + def __init__ (self, domainName): + self.domainName = domainName + def __str__ (self): + return self.comment % { "domainName" : self.domainName } + + +# ----------------------------------------------------- +""" Syntax error Exceptions used by parser""" + +class MySyntaxError(MySyntaxProblems) : + """ Syntax error Exceptions used by parser""" + pass + +class MySyntaxWarning(MySyntaxProblems) : + """ Syntax warning Exceptions used by parser""" + pass + +class IndentationSyntaxError(MySyntaxError) : + comment = """syntax error in %(line)s has no father element. + You can only increment indentation by one tabutation per line")""" + +class EmptyLineWarning(MySyntaxWarning): + comment = "warning : %(line)s is an empty line and has been ommited" + +class CommentWarning(MySyntaxWarning): + comment = "warning : %(line)s is a commentary and has been ommited" + +class ChildNotPermitedError(MySyntaxError): + def __init__(self, line, fatherElement, childElement): + self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " + childElement.tag + " child." + super(ChildNotPermitedError, self).__init__(line) + + +class UnknownElementTypeError(MySyntaxError): + comment = " error in line %(line)s , not known element type were matched " + +class SpaceInIndentationError(MySyntaxError): + comment = " error in ,%(line)s space is not permited in indentation" + + +# ============================================ +"""Class creating the DOM elements from a stream""" +# ============================================ + +class ElementsFactory(object) : + """Element factory, return an instance of the first matching element + + Test each element list in elementClass and instanciate it if it's methode match returns True + The method match is called with input line as argument + """ + def __init__ (self): + self.elementClass = [ + EmptyLine , + Commentary, + GroupDomain, + Domain, + Path, + GroupConfiguration, + Configuration, + Operator, + Rule, + GroupPath + ] + + def createElementFromLine (self, line) : + """return an instance of the first matching element + + Test each element list in elementClass and instanciate it if it's methode match returns True + The method match is called with the argument line. + Raise UnknownElementTypeError if no element matched. + """ + for element in self.elementClass : + if element.match(line) : + # print (line + element.__class__.__name__) + return element(line) + # if we have not find any + raise UnknownElementTypeError(line) + +#------------------------------------------------------ + +class Parser(object) : + """Class implementing the parser""" + def __init__(self): + self.rankPattern = re.compile(r"^([\t ]*)(.*)") + self.elementFactory = ElementsFactory() + self.previousRank = 0 + + def __parseLine__(self, line): + + rank, rest = self.__getRank__(line) + + # instanciate the coresponding element + element = self.elementFactory.createElementFromLine(rest) + + self.__checkIndentation__(rank) + + return rank, element + + def __getRank__(self, line): + """return the rank, the name and the option of the input line + +the rank is the number of tabulation (\t) at the line beginning. +the rest is the rest of the line.""" + # split line in rank and rest + rank = self.rankPattern.match(line) + if rank : + rank, rest = rank.group(1, 2) + else : + raise MySyntaxError(line) + + # check for empty line + if rest == "" : + raise EmptyLineWarning(line) + + # check for space in indentation + if rank.find(" ") > -1 : + raise SpaceInIndentationError(line) + + rank = len (rank) + 1 # rank starts at 1 + + + return rank, rest + + + def __checkIndentation__(self, rank): + """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError""" + if (rank > self.previousRank + 1) : + raise IndentationSyntaxError() + self.previousRank = rank + + def parse(self, stream, verbose=False): + """parse a stream, usually a opened file""" + myroot = Root("root") + context = [myroot] # root is element of rank 0 + warnings = "" + + for num, line in enumerate(stream): + try: + rank, myelement = self.__parseLine__(line) + + while len(context) > rank : + context.pop() + context.append(myelement) + context[-2].addChild(myelement) + + except MySyntaxWarning, ex: + ex.setLine(line, num + 1) + if verbose : + print >>sys.stderr, ex + + except MySyntaxError, ex : + ex.setLine(line, num + 1) + raise + + return myroot + diff --git a/tools/xmlGenerator/PFWScriptGenerator.py b/tools/xmlGenerator/PFWScriptGenerator.py index 3e9eb74..d1996d1 100755 --- a/tools/xmlGenerator/PFWScriptGenerator.py +++ b/tools/xmlGenerator/PFWScriptGenerator.py @@ -1,7 +1,6 @@ #!/usr/bin/python2 -# -*-coding:utf-8 -* -# Copyright (c) 2011-2014, Intel Corporation +# Copyright (c) 2015, Intel Corporation # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -29,1061 +28,73 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import EddParser +from PfwBaseTranslator import PfwBaseTranslator - -import re +import argparse import sys -import copy -import imp -from itertools import izip -from itertools import imap - -try: - import argparse -except ImportError: - import optparse - -# ===================================================================== -""" Context classes, used during propagation and the "to PFW script" step """ -# ===================================================================== - -class PropagationContextItem(list) : - """Handle an item during the propagation step""" - def __copy__(self): - """C.__copy__() -> a shallow copy of C""" - return self.__class__(self) - -class PropagationContextElement(PropagationContextItem) : - """Handle an Element during the propagation step""" - def getElementsFromName(self, name): - matchingElements = [] - for element in self : - if element.getName() == name : - matchingElements.append(element) - return matchingElements - - -class PropagationContextOption(PropagationContextItem) : - """Handle an Option during the propagation step""" - def getOptionItems (self, itemName): - items = [] - for options in self : - items.append(options.getOption(itemName)) - return items - - -class PropagationContext() : - """Handle the context during the propagation step""" - def __init__(self, propagationContext=None) : - - if propagationContext == None : - self._context = { - "DomainOptions" : PropagationContextOption() , - "Configurations" : PropagationContextElement() , - "ConfigurationOptions" : PropagationContextOption() , - "Rules" : PropagationContextElement() , - "PathOptions" : PropagationContextOption() , - } - else : - self._context = propagationContext - - def copy(self): - """return a copy of the context""" - contextCopy = self._context.copy() - - for key in iter(self._context) : - contextCopy[key] = contextCopy[key].__copy__() - - return self.__class__(contextCopy) - - def getDomainOptions (self): - return self._context["DomainOptions"] - - def getConfigurations (self): - return self._context["Configurations"] - - def getConfigurationOptions (self): - return self._context["ConfigurationOptions"] - - def getRules (self): - return self._context["Rules"] - - def getPathOptions (self): - return self._context["PathOptions"] - -# --------------------------------------------------------------------------- - -class PFWScriptContext (): - """handle the context during the PFW script generation""" - - def __init__(self, prefixIncrease=" ") : - self._context = { - "Prefix" : "" , - "DomainName" : "" , - "ConfigurationName" : "" , - "SequenceAwareness" : False , - } - self._prefixIncrease = prefixIncrease - - def increasePrefix(self) : - self._context["Prefix"] = self._prefixIncrease + self._context["Prefix"] - - def getNewLinePrefix(self) : - """return a prefix with decorative new line - - return r"\"+"\n"+" "*increased prefix length""" - return "\\\n" + self._prefixIncrease + " "* len(self.getPrefix()) - - def copy(self) : - copy = PFWScriptContext () - copy._context = self._context.copy() - return copy - - def setDomainName (self, name) : - self._context["DomainName"] = name - - def setConfigurationName (self, name) : - self._context["ConfigurationName"] = name - - def setSequenceAwareness (self, sequenceAwareness) : - self._context["SequenceAwareness"] = sequenceAwareness - - def getPrefix(self): - return self._context["Prefix"] - - def getDomainName (self): - return self._context["DomainName"] - - def getConfigurationName(self): - return self._context["ConfigurationName"] - - def getSequenceAwareness(self): - return self._context["SequenceAwareness"] - -# ===================================================== -"""Element option container""" -# ===================================================== - -class Options () : - """handle element options""" - def __init__(self, options=[], optionNames=[]) : - self.options = dict(izip(optionNames, options)) - # print(options,optionNames,self.options) - - - def __str__(self) : - ops2str = [] - for name, argument in self.options.items() : - ops2str.append(str(name) + "=\"" + str(argument) + "\"") - - return " ".join(ops2str) - - def getOption(self, name): - """get option by its name, if it does not exist return empty string""" - return self.options.get(name, "") - - def setOption(self, name, newOption): - """set option by its name""" - self.options[name] = newOption - - def copy (self): - """D.copy() -> a shallow copy of D""" - copy = Options() - copy.options = self.options.copy() - return copy - -# ==================================================== -"""Definition of all element class""" -# ==================================================== - -class Element(object): - """ implement a basic element - - It is the class base for all other elements as Domain, Configuration...""" - tag = "unknown" - optionNames = ["Name"] - childWhiteList = [] - optionDelimiter = " " - - def __init__(self, line=None) : - - if line == None : - self.option = Options([], self.optionNames) - else : - self.option = self.optionFromLine(line) - - self.children = [] - - def optionFromLine(self, line) : - # get ride of spaces - line = line.strip() - - options = self.extractOptions(line) - - return Options(options, self.optionNames) - - def extractOptions(self, line) : - """return the line splited by the optionDelimiter atribute - - Option list length is less or equal to the optionNames list length - """ - options = line.split(self.optionDelimiter, len(self.optionNames) - 1) - - # get ride of leftover spaces - optionsStrip = list(imap(str.strip, options)) - - return optionsStrip - - def addChild(self, child, append=True) : - """ A.addChid(B) -> add B to A child list if B class name is in A white List""" - try: - # Will raise an exception if this child is not in the white list - self.childWhiteList.index(child.__class__.__name__) - # If no exception was raised, add child to child list - - if append : - self.children.append(child) - else : - self.children.insert(0, child) - - except ValueError: - # the child class is not in the white list - raise ChildNotPermitedError("", self, child) - - def addChildren(self, children, append=True) : - """Add a list of child""" - if append: - # Add children at the end of the child list - self.children.extend(children) - else: - # Add children at the begining of the child list - self.children = children + self.children - - def childrenToString(self, prefix=""): - """return raw printed children """ - body = "" - for child in self.children : - body = body + child.__str__(prefix) - - return body - - def __str__(self, prefix="") : - """return raw printed element""" - selfToString = prefix + " " + self.tag + " " + str(self.option) - return selfToString + "\n" + self.childrenToString(prefix + "\t") - - def extractChildrenByClass(self, classTypeList) : - """return all children whose class is in the list argument - - return a list of all children whose class in the list "classTypeList" (second arguments)""" - selectedChildren = [] - - for child in self.children : - for classtype in classTypeList : - if child.__class__ == classtype : - selectedChildren.append(child) - break - return selectedChildren - - def propagate (self, context=PropagationContext()): - """call the propagate method of all children""" - for child in self.children : - child.propagate(context) - - def getName(self): - """return name option value. If none return "" """ - return self.option.getOption("Name") - - def setName(self, name): - self.option.setOption("Name", name) - - def toPFWScript (self, context=PFWScriptContext()) : - script = "" - for child in self.children : - script += child.toPFWScript(context) - return script - - -# ---------------------------------------------------------- - -class ElementWithTag (Element): - """Element of this class are declared with a tag => line == "tag: .*" """ - def extractOptions(self, line) : - lineWithoutTag = line.split(":", 1)[-1].strip() - options = super(ElementWithTag, self).extractOptions(lineWithoutTag) - return options - -# ---------------------------------------------------------- - -class ElementWithInheritance(Element): - def propagate (self, context=PropagationContext) : - """propagate some proprieties to children""" - - # copy the context so that everything that hapend next will only affect - # children - contextCopy = context.copy() - - # check for inheritance - self.Inheritance(contextCopy) - - # call the propagate method of all children - super(ElementWithInheritance, self).propagate(contextCopy) - - -class ElementWithRuleInheritance(ElementWithInheritance): - """class that will give to its children its rules""" - def ruleInheritance(self, context): - """Add its rules to the context and get context rules""" - - # extract all children rule and operator - childRules = self.extractChildrenByClass([Operator, Rule]) - - # get context rules - contextRules = context.getRules() - - # adopt rules of the beginning of the context - self.addChildren(contextRules, append=False) - - # add previously extract rules to the context - contextRules += childRules - - -# ---------------------------------------------------------- - -class EmptyLine (Element) : - """This class represents an empty line. - - Will raise "EmptyLineWarning" exception at instanciation.""" - - tag = "emptyLine" - match = re.compile(r"[ \t]*\n?$").match - def __init__ (self, line): - raise EmptyLineWarning(line) - -# ---------------------------------------------------------- - -class Commentary(Element): - """This class represents a commentary. - - Will raise "CommentWarning" exception at instanciation.""" - - tag = "commentary" - optionNames = ["comment"] - match = re.compile(r"#").match - def __init__ (self, line): - raise CommentWarning(line) - -# ---------------------------------------------------------- - -class Path (ElementWithInheritance) : - """class implementing the "path = value" concept""" - tag = "path" - optionNames = ["Name", "value"] - match = re.compile(r".+=").match - optionDelimiter = "=" - PFWCommandParameter = "setParameter" - - def toPFWScript (self, context=PFWScriptContext()) : - - return context.getPrefix() + \ - self.PFWCommandParameter + " " + \ - self.getName() + " '" + \ - self.option.getOption("value") + "'\n" - - def Inheritance (self, context) : - """check for path name inheritance""" - self.OptionsInheritance(context) - - def OptionsInheritance (self, context) : - """make configuration name inheritance """ - - context.getPathOptions().append(self.option.copy()) - self.setName("/".join(context.getPathOptions().getOptionItems("Name"))) - - -class GroupPath (Path, ElementWithTag) : - tag = "component" - match = re.compile(tag + r" *:").match - optionNames = ["Name"] - childWhiteList = ["Path", "GroupPath"] - - def toPFWScript (self, pfwScriptContext) : - script = "" - - configurationChildren = self.extractChildrenByClass([GroupPath, Path]) - - for configurationChild in configurationChildren : - # add configuration settings - script += configurationChild.toPFWScript(pfwScriptContext) - - return script - - def getPathNames (self) : - """Return the list of all path child name""" - - pathNames = [] - - paths = self.extractChildrenByClass([Path]) - for path in paths : - pathNames.append(path.getName()) - - groupPaths = self.extractChildrenByClass([GroupPath]) - for groupPath in groupPaths : - pathNames += groupPath.getPathNames() - - return pathNames - -# ---------------------------------------------------------- - -class Rule (Element) : - """class implementing the rule concept - - A rule is composed of a criterion, a rule type and an criterion state. - It should not have any child and is propagated to all configuration in parent descendants. - """ - - tag = "rule" - optionNames = ["criterion", "type", "element"] - match = re.compile(r"[a-zA-Z0-9_.]+ +(Is|IsNot|Includes|Excludes) +[a-zA-Z0-9_.]+").match - childWhiteList = [] - - def PFWSyntax (self, prefix=""): - - script = prefix + \ - self.option.getOption("criterion") + " " + \ - self.option.getOption("type") + " " + \ - self.option.getOption("element") - - return script - - -class Operator (Rule) : - """class implementing the operator concept - - An operator contains rules and other operators - It is as rules propagated to all configuration children in parent descendants. - It should only have the name ANY or ALL to be understood by PFW. - """ - - tag = "operator" - optionNames = ["Name"] - match = re.compile(r"ANY|ALL").match - childWhiteList = ["Rule", "Operator"] - - PFWCommandRule = "setRule" - syntax = { "ANY" : "Any" , "ALL" : "All"} - - def toPFWScript (self, context) : - """ return a pfw commands generated from him and its child options""" - script = "" - - # add the command name (setRule) - script += context.getPrefix() + \ - self.PFWCommandRule + " " + \ - context.getDomainName() + " " + \ - context.getConfigurationName() + " " - - # add the rule - script += self.PFWSyntax (context.getNewLinePrefix()) - - script += "\n" - - return script - - def PFWSyntax (self, prefix=""): - """ return a pfw rule (ex : "Any{criterion1 is state1}") generated from "self" and its children options""" - script = "" - - script += prefix + \ - self.syntax[self.getName()] + "{ " - - rules = self.extractChildrenByClass([Rule, Operator]) - - PFWRules = [] - for rule in rules : - PFWRules.append(rule.PFWSyntax(prefix + " ")) - - script += (" , ").join(PFWRules) - - script += prefix + " }" - - return script - -# ---------------------------------------------------------- - -class Configuration (ElementWithRuleInheritance, ElementWithTag) : - tag = "configuration" - optionNames = ["Name"] - match = re.compile(r"conf *:").match - childWhiteList = ["Rule", "Operator", "Path", "GroupPath"] - - PFWCommandConfiguration = "createConfiguration" - PFWCommandElementSequence = "setElementSequence" - PFWCommandSequenceAware = "setSequenceAwareness" - - PFWCommandRestoreConfiguration = "restoreConfiguration" - PFWCommandSaveConfiguration = "saveConfiguration" - - def composition (self, context): - """make all needed composition - - Composition is the fact that group configuration with the same name defined - in a parent will give their rule children to this configuration - """ - - name = self.getName() - sameNameConf = context.getConfigurations().getElementsFromName(name) - - sameNameConf.reverse() - - for configuration in sameNameConf : - # add same name configuration rule children to self child list - self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False) - - - def propagate (self, context=PropagationContext) : - """propagate proprieties to children - - make needed compositions, join ancestor name to its name, - and add rules previously defined rules""" - - # make all needed composition - self.composition(context) - - super(Configuration, self).propagate(context) - - def Inheritance (self, context) : - """make configuration name and rule inheritance""" - # check for configuration name inheritance - self.OptionsInheritance(context) - - # check for rule inheritance - self.ruleInheritance(context) - - def OptionsInheritance (self, context) : - """make configuration name inheritance """ - - context.getConfigurationOptions().append(self.option.copy()) - self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name"))) - - - def getRootPath (self) : - - paths = self.extractChildrenByClass([Path, GroupPath]) - - rootPath = GroupPath() - rootPath.addChildren(paths) - - return rootPath - - def getConfigurableElements (self) : - """return all path name defined in this configuration""" - - return self.getRootPath().getPathNames() - - def toPFWScript(self, pfwScriptContext) : - """Output the PFW commands needed to recreate this configuration - - The PFW commands outputed will recreate this configuration if run - on a PFW instance""" - - script = "" - - # Copy and update pfwScriptContext for this configuration - pfwScriptContextAux = pfwScriptContext.copy() - pfwScriptContextAux.setConfigurationName (self.getName()) - - # Add the command to create the configuration - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandConfiguration + " " + \ - pfwScriptContextAux.getDomainName() + " " + \ - pfwScriptContextAux.getConfigurationName() + "\n" - - # encrease prefix - pfwScriptContextAux.increasePrefix() - # Create a rootRule - ruleChildren = self.extractChildrenByClass([Rule, Operator]) +class PfwScriptTranslator(PfwBaseTranslator): - # Do not create a root rule if there is only one fist level Operator rule - if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator : - ruleroot = ruleChildren[0] - - else : - ruleroot = Operator() - ruleroot.setName("ALL") - ruleroot.addChildren(ruleChildren) - - - # Add the command to create the rules of this configuration - script += ruleroot.toPFWScript(pfwScriptContextAux) - - - # Add the command to restore this configuration - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandRestoreConfiguration + " " + \ - pfwScriptContextAux.getDomainName() + " " + \ - pfwScriptContextAux.getConfigurationName() + "\n" - - # Copy pfwScriptContextAux and increase the prefix - contextAux = pfwScriptContextAux.copy() - contextAux.increasePrefix() - - # add the parameter settings for this configuration - paths = self.extractChildrenByClass([Path, GroupPath]) - for path in paths : - script += path.toPFWScript(contextAux) - - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandSaveConfiguration + " " + \ - pfwScriptContextAux.getDomainName() + " " + \ - pfwScriptContextAux.getConfigurationName() + "\n" - - # if domain is sequence aware - if pfwScriptContextAux.getSequenceAwareness() : - - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandElementSequence + " " + \ - pfwScriptContextAux.getDomainName() + " " + \ - pfwScriptContextAux.getConfigurationName() + " " - - for path in paths : - script += pfwScriptContextAux.getNewLinePrefix() + \ - path.getName() - script += "\n" - - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandSequenceAware + " "\ - + pfwScriptContextAux.getDomainName() + " true \n" - - # for lisibility - script += "\n" - - return script - - def copy (self) : - """return a shallow copy of the configuration""" - - # create configuration or subclass copy - confCopy = self.__class__() - - # add children - confCopy.children = list(self.children) - - # add option - confCopy.option = self.option.copy() - - return confCopy - -class GroupConfiguration (Configuration) : - tag = "GroupConfiguration" - optionNames = ["Name"] - match = re.compile(r"(supConf|confGroup|confType) *:").match - childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"] - - def composition (self, context) : - """add itself in context for configuration composition - - Composition is the fact that group configuration with the same name defined - in a parent will give their rule children to this configuration - """ - - # copyItself - selfCopy = self.copy() - - # make all needed composition - super(GroupConfiguration, self).composition(context) - - # add the copy in context for futur configuration composition - context.getConfigurations().append(selfCopy) - - - def getConfigurableElements (self) : - """return a list. Each elements consist of a list of configurable element of a configuration - - return a list consisting of all configurable elements for each configuration. - These configurable elements are organized in a list""" - configurableElements = [] - - configurations = self.extractChildrenByClass([Configuration]) - for configuration in configurations : - configurableElements.append(configuration.getConfigurableElements()) - - groudeConfigurations = self.extractChildrenByClass([GroupConfiguration]) - for groudeConfiguration in groudeConfigurations : - configurableElements += groudeConfiguration.getConfigurableElements() - - return configurableElements - - def toPFWScript (self, pfwScriptContext) : - script = "" - - configurationChildren = self.extractChildrenByClass([Configuration, GroupConfiguration]) - - for configurationChild in configurationChildren : - # add configuration settings - script += configurationChild.toPFWScript(pfwScriptContext) - - return script - -# ---------------------------------------------------------- - -class Domain (ElementWithRuleInheritance, ElementWithTag) : - tag = "domain" - sequenceAwareKeyword = "sequenceAware" - - match = re.compile(r"domain *:").match - optionNames = ["Name", sequenceAwareKeyword] - childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"] - - PFWCommandConfigurableElement = "addElement" - PFWCommandDomain = "createDomain" - - def propagate (self, context=PropagationContext) : - """ propagate name, sequenceAwareness and rule to children""" - - # call the propagate method of all children - super(Domain, self).propagate(context) - - self.checkConfigurableElementUnicity() - - def Inheritance (self, context) : - """check for domain name, sequence awarness and rules inheritance""" - # check for domain name and sequence awarness inheritance - self.OptionsInheritance(context) - - # check for rule inheritance - self.ruleInheritance(context) - - def OptionsInheritance(self, context) : - """ make domain name and sequence awareness inheritance - - join to the domain name all domain names defined in context and - if any domain in context is sequence aware, set sequenceAwareness to True""" - - # add domain options to context - context.getDomainOptions().append(self.option.copy()) - - # set name to the junction of all domain name in context - self.setName(".".join(context.getDomainOptions().getOptionItems("Name"))) - - # get sequenceAwareness of all domains in context - sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword) - # or operation on all booleans in sequenceAwareList - sequenceAwareness = False - for sequenceAware in sequenceAwareList : - sequenceAwareness = sequenceAwareness or sequenceAware - # current domain sequenceAwareness = sequenceAwareness - self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness) - - - def extractOptions(self, line) : - """Extract options from the definition line""" - options = super(Domain, self).extractOptions(line) - - sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword) - - # translate the keyword self.sequenceAwareKeyword if specified to boolean True, - # to False otherwise - try : - if options[sequenceAwareIndex] == self.sequenceAwareKeyword : - options[sequenceAwareIndex] = True - else: - options[sequenceAwareIndex] = False - except IndexError : - options = options + [None] * (sequenceAwareIndex - len(options)) + [False] - return options - - def getRootConfiguration (self) : - """return the root configuration group""" - configurations = self.extractChildrenByClass([Configuration, GroupConfiguration]) - - configurationRoot = GroupConfiguration() - - configurationRoot.addChildren(configurations) - - return configurationRoot - - def checkConfigurableElementUnicity (self): - """ check that all configurable elements defined in child configuration are the sames""" - - # get a list. Each elements of is the configurable element list of a configuration - configurableElementsList = self.getRootConfiguration().getConfigurableElements() - - # if at least two configurations in the domain - if len(configurableElementsList) > 1 : - - # get first configuration configurable element list sort - configurableElementsList0 = list(configurableElementsList[0]) - configurableElementsList0.sort() - - for configurableElements in configurableElementsList : - # sort current configurable element list - auxConfigurableElements = list(configurableElements) - auxConfigurableElements.sort() - - if auxConfigurableElements != configurableElementsList0 : - # if different, 2 configurations those not have the same configurable element list - # => one or more configurable element is missing in one of the 2 configuration - raise UndefinedParameter(self.getName()) - - - def toPFWScript (self, pfwScriptContext=PFWScriptContext()): - script = "" - - domainName = self.getName() - - - script += pfwScriptContext.getPrefix() + \ - self.PFWCommandDomain + " " + \ - domainName + "\n" - - # get sequenceAwareness of this domain - sequenceAwareness = self.option.getOption(self.sequenceAwareKeyword) - - # Copy and update pfwScriptContext for this domain - pfwScriptContextAux = pfwScriptContext.copy() - pfwScriptContextAux.setDomainName(domainName) - pfwScriptContextAux.setSequenceAwareness(sequenceAwareness) - pfwScriptContextAux.increasePrefix() - - # get configurable elements - configurationRoot = self.getRootConfiguration() - configurableElementsList = configurationRoot.getConfigurableElements() - - # add configurable elements - if len(configurableElementsList) != 0 : - - for configurableElement in configurableElementsList[0] : - - script += pfwScriptContextAux.getPrefix() + \ - self.PFWCommandConfigurableElement + " " + \ - domainName + " " + \ - configurableElement + "\n" - - # new line to be more lisible : - script += "\n" - - # add configuration settings - script += configurationRoot.toPFWScript(pfwScriptContextAux) - - # to be more lisible : - script += "\n" - - return script - - -class GroupDomain (Domain) : - tag = "groupDomain" - match = re.compile(r"(supDomain|domainGroup) *:").match - childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"] - - def toPFWScript (self, context={}): - script = "" - children = self.extractChildrenByClass([Domain, GroupDomain]) - - for child in children : - script += child.toPFWScript(context) - - return script - -# ---------------------------------------------------------- - -class Root(Element): - tag = "root" - childWhiteList = ["Domain", "GroupDomain"] - - -# =========================================== -""" Syntax error Exceptions""" -# =========================================== - -class MySyntaxProblems(SyntaxError) : - comment = "syntax error in %(line)s " - - def __init__(self, line=None, num=None): - self.setLine(line, num) - - def __str__(self): - - if self.line : - self.comment = self.comment % {"line" : repr(self.line)} - if self.num : - self.comment = "Line " + str(self.num) + ", " + self.comment - return self.comment - - def setLine (self, line, num): - self.line = str(line) - self.num = num - - -# --------------------------------------------------------- - -class MyPropagationError(MySyntaxProblems) : - """ Syntax error Exceptions used in the propagation step""" - pass - -class UndefinedParameter(MyPropagationError) : - comment = "Configurations in domain '%(domainName)s' do not all set the same parameters " - def __init__ (self, domainName): - self.domainName = domainName - def __str__ (self): - return self.comment % { "domainName" : self.domainName } - - -# ----------------------------------------------------- -""" Syntax error Exceptions used by parser""" - -class MySyntaxError(MySyntaxProblems) : - """ Syntax error Exceptions used by parser""" - pass - -class MySyntaxWarning(MySyntaxProblems) : - """ Syntax warning Exceptions used by parser""" - pass - -class IndentationSyntaxError(MySyntaxError) : - comment = """syntax error in %(line)s has no father element. - You can only increment indentation by one tabutation per line")""" - -class EmptyLineWarning(MySyntaxWarning): - comment = "warning : %(line)s is an empty line and has been ommited" - -class CommentWarning(MySyntaxWarning): - comment = "warning : %(line)s is a commentary and has been ommited" - -class ChildNotPermitedError(MySyntaxError): - def __init__(self, line, fatherElement, childElement): - self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " + childElement.tag + " child." - super(ChildNotPermitedError, self).__init__(line) - - -class UnknownElementTypeError(MySyntaxError): - comment = " error in line %(line)s , not known element type were matched " - -class SpaceInIndentationError(MySyntaxError): - comment = " error in ,%(line)s space is not permited in indentation" - - -# ============================================ -"""Class creating the DOM elements from a stream""" -# ============================================ - -class ElementsFactory(object) : - """Element factory, return an instance of the first matching element - - Test each element list in elementClass and instanciate it if it's methode match returns True - The method match is called with input line as argument - """ - def __init__ (self): - self.elementClass = [ - EmptyLine , - Commentary, - GroupDomain, - Domain, - Path, - GroupConfiguration, - Configuration, - Operator, - Rule, - GroupPath - ] - - def createElementFromLine (self, line) : - """return an instance of the first matching element - - Test each element list in elementClass and instanciate it if it's methode match returns True - The method match is called with the argument line. - Raise UnknownElementTypeError if no element matched. - """ - for element in self.elementClass : - if element.match(line) : - # print (line + element.__class__.__name__) - return element(line) - # if we have not find any - raise UnknownElementTypeError(line) - -#------------------------------------------------------ - -class Parser(object) : - """Class implementing the parser""" def __init__(self): - self.rankPattern = re.compile(r"^([\t ]*)(.*)") - self.elementFactory = ElementsFactory() - self.previousRank = 0 - - def __parseLine__(self, line): - - rank, rest = self.__getRank__(line) - - # instanciate the coresponding element - element = self.elementFactory.createElementFromLine(rest) - - self.__checkIndentation__(rank) - - return rank, element - - def __getRank__(self, line): - """return the rank, the name and the option of the input line - -the rank is the number of tabulation (\t) at the line beginning. -the rest is the rest of the line.""" - # split line in rank and rest - rank = self.rankPattern.match(line) - if rank : - rank, rest = rank.group(1, 2) - else : - raise MySyntaxError(line) - - # check for empty line - if rest == "" : - raise EmptyLineWarning(line) - - # check for space in indentation - if rank.find(" ") > -1 : - raise SpaceInIndentationError(line) - - rank = len (rank) + 1 # rank starts at 1 - - - return rank, rest - - - def __checkIndentation__(self, rank): - """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError""" - if (rank > self.previousRank + 1) : - raise IndentationSyntaxError() - self.previousRank = rank - - def parse(self, stream, verbose=False): - """parse a stream, usually a opened file""" - myroot = Root("root") - context = [myroot] # root is element of rank 0 - warnings = "" - - for num, line in enumerate(stream): - try: - rank, myelement = self.__parseLine__(line) - - while len(context) > rank : - context.pop() - context.append(myelement) - context[-2].addChild(myelement) - - except MySyntaxWarning, ex: - ex.setLine(line, num + 1) - if verbose : - print >>sys.stderr, ex - - except MySyntaxError, ex : - ex.setLine(line, num + 1) - raise - - return myroot - -# ============================ -# command line argument parser -# ============================ + super(PfwScriptTranslator, self).__init__() + + self._script = [] + + def getScript(self): + return self._script + + def _doCreateDomain(self, name): + self._script.append( + "{cmd} {domain}".format( + cmd="createDomain", + domain=name)) + + def _doSetSequenceAware(self): + self._script.append( + "{cmd} {domain} {aware}".format( + cmd="setSequenceAwareness", + domain=self._ctx_domain, + aware="true")) + + def _doAddElement(self, path): + self._script.append( + "{cmd} {domain} {path}".format( + cmd="addElement", + domain=self._ctx_domain, + path=path)) + + def _doCreateConfiguration(self, name): + self._script.append( + "{cmd} {domain} {config}".format( + cmd="createConfiguration", + domain=self._ctx_domain, + config=name)) + + def _doSetElementSequence(self, paths): + self._script.append( + "{cmd} {domain} {config} {paths}".format( + cmd="setElementSequence", + domain=self._ctx_domain, + config=self._ctx_configuration, + paths=" ".join(paths))) + + def _doSetRule(self, rule): + self._script.append( + "{cmd} {domain} {config} {rule}".format( + cmd="setRule", + domain=self._ctx_domain, + config=self._ctx_configuration, + rule=rule)) + + def _doSetParameter(self, path, value): + self._script.append( + "{cmd} {domain} {config} {path} '{value}'".format( + cmd="setConfigurationParameter", + domain=self._ctx_domain, + config=self._ctx_configuration, + path=path, + value=value)) class ArgparseArgumentParser(object) : """class that parse command line arguments with argparse library @@ -1093,109 +104,35 @@ class ArgparseArgumentParser(object) : myArgParser = argparse.ArgumentParser(description='Process domain scripts.') - myArgParser.add_argument('inputFile', nargs='?', - type=argparse.FileType('r'), default=sys.stdin, - help="the domain script file, default stdin") + myArgParser.add_argument('input', nargs='?', + type=argparse.FileType('r'), default=sys.stdin, + help="the domain script file, default stdin") myArgParser.add_argument('-o', '--output', - dest="outputFile", - type=argparse.FileType('w'), default=sys.stdout, - help="the output file, default stdout") + type=argparse.FileType('w'), default=sys.stdout, + help="the output file, default stdout") myArgParser.add_argument('-d', '--debug', - dest="debugFlag", - action='store_true', - help="print debug warnings") - + action='store_true', + help="print debug warnings") - outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False) - - outputFormatGroupe.add_argument('--pfw', - dest="pfwFlag", - action='store_true', - help="output pfw commands (default)") - outputFormatGroupe.add_argument('--raw', - dest="rawFlag", - action='store_true', - help="output raw domain tree (DEBUG ONLY)") + myArgParser.add_argument('--output-kind', + choices=['pfw', 'raw'], + default='pfw', + help="output kind; can be either 'raw' (debug only) or 'pfw' (pfw commands; default choice)") # process command line arguments options = myArgParser.parse_args() # maping to atributs - self.inputFile = options.inputFile - self.output = options.outputFile - - self.debug = options.debugFlag + self.input = options.input + self.output = options.output - if not (options.pfwFlag or options.rawFlag) : - # --pfw is default if none provided - self.pfw = True - else : - self.pfw = options.pfwFlag - self.raw = options.rawFlag - - -class OptParseArgumentParser(object) : - """class that parse command line arguments with optparse library - - result of parsing are the class atributs""" - def __init__(self) : + self.debug = options.debug - myOptParser = optparse.OptionParser(usage="usage: [-h] [-d] [--pfw | --raw] " - "[-o OUTPUTFILE] [INPUTFILE]", - description="Process domain scripts") + self.output_kind = options.output_kind - myOptParser.add_option('-o', '--output', - dest="outputFile", metavar="FILE", - help="the output file, default stdout") - - myOptParser.add_option('-d', '--debug', - dest="debugFlag", - action='store_true', - help="print debug warnings") - - - outputFormatGroupe = optparse.OptionGroup(myOptParser, "output format") - - outputFormatGroupe.add_option('--pfw', - dest="pfwFlag", - action='store_true', - help="output pfw commands (default)") - outputFormatGroupe.add_option('--raw', - dest="rawFlag", - action='store_true', - help="output raw domain tree (DEBUG ONLY)") - - - # process command line arguments - (options, args) = myOptParser.parse_args() - - # If no input file provided, use default one - if len(args) == 0: - args = [None] - - # mapping to attributes - self.inputFile = self.open_secured(args[0], 'r') or sys.stdin - self.output = self.open_secured(options.outputFile, 'w') or sys.stdout - - self.debug = options.debugFlag - - if not (options.pfwFlag or options.rawFlag) : - # --pfw is default if none provided - # TODO: find a way to do that with argparse directly - self.pfw = True - else : - self.pfw = options.pfwFlag - self.raw = options.rawFlag - - @staticmethod - def open_secured(file, openMode="r"): - if file: - return open(file, openMode) - - return None # ============== # main function @@ -1203,46 +140,36 @@ class OptParseArgumentParser(object) : def printE(s): """print in stderr""" - print >>sys.stderr, str(s) + sys.stderr.write(str(s)) def main (): - # Get command line arguments - try: - imp.find_module("argparse") - - except ImportError: - printE("Warning: unable to import argparse module, fallback to optparse") - # Using optparse - options = OptParseArgumentParser() + options = ArgparseArgumentParser() - else: - # Using argparse - options = ArgparseArgumentParser() - - myparser = Parser() + myparser = EddParser.Parser() try: - myroot = myparser.parse(options.inputFile, options.debug) + myroot = myparser.parse(options.input, options.debug) - except MySyntaxError, ex : + except EddParser.MySyntaxError as ex: printE(ex) printE("EXIT ON FAILURE") - exit (2) - else : - if options.raw : - options.output.write(str(myroot)) - else : - try : - myroot.propagate() + exit(2) + + if options.output_kind == 'raw': + options.output.write(str(myroot)) + else: + try: + myroot.propagate() - except MyPropagationError, ex : - printE(ex) - printE("EXIT ON FAILURE") - exit(1) + except EddParser.MyPropagationError, ex : + printE(ex) + printE("EXIT ON FAILURE") + exit(1) - else: - if options.pfw : - options.output.write(myroot.toPFWScript()) + if options.output_kind == 'pfw': + translator = PfwScriptTranslator() + myroot.translate(translator) + options.output.write("\n".join(translator.getScript())) # execute main function if the python interpreter is running this module as the main program if __name__ == "__main__" : diff --git a/tools/xmlGenerator/PfwBaseTranslator.py b/tools/xmlGenerator/PfwBaseTranslator.py new file mode 100644 index 0000000..0d08565 --- /dev/null +++ b/tools/xmlGenerator/PfwBaseTranslator.py @@ -0,0 +1,177 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +class PfwException(Exception): + pass + +class PfwBaseTranslator(object): + """Abstract Pfw Translator class + + The protocol presented by this class allows the creation of + parameter-framework settings: domains, configurations rules, etc. + + Some methods must be called within a context; e.g. when 'addElement' is + called, the element is added to the domain that was last created through + 'createDomain' + + Derived classed should only implemented the backend methods, prefixed by an + underscore.""" + + def __init__(self): + self._ctx_domain = '' + self._ctx_configuration = '' + self._ctx_sequence_aware = False + self._ctx_command = '' + + self._domain_valid = False + self._configuration_valid = False + + def _getContext(self): + return { + 'domain': self._ctx_domain, + 'configuration': self._ctx_configuration, + 'sequence_aware': self._ctx_sequence_aware, + 'command': self._ctx_command} + + def _check(self, func): + """Check and handles exceptions + + Returns False if an exception occured and was properly caught, + True otherwise""" + def wrapped(*args, **kwargs): + try: + func(*args, **kwargs) + except PfwException as ex: + self._handleException(ex) + return False + + return True + + return wrapped + + def createDomain(self, name, sequence_aware=False): + """Create a domain with a given name and optionally set its sequence + awareness""" + + self._ctx_command = "createDomain" + self._ctx_domain = name + self._ctx_configuration = '' + self._ctx_sequence_aware = sequence_aware + self._domain_valid = True + + if not self._check(self._doCreateDomain)(name): + self._domain_valid = False + elif sequence_aware: + self._check(self._doSetSequenceAware)() + + def addElement(self, path): + """Add a configurable element to the current domain""" + + self._ctx_command = "addElement" + + if not self._domain_valid: + return + + self._check(self._doAddElement)(path) + + def createConfiguration(self, name): + """Create a configuration for the current domain""" + + self._ctx_command = "createConfiguration" + self._ctx_configuration = name + self._configuration_valid = True + + if not self._domain_valid: + self._configuration_valid = False + return + + if not self._check(self._doCreateConfiguration)(name): + self._configuration_valid = False + + def setElementSequence(self, paths): + """Set the element sequence (if applicable, e.g. if the domain is + sequence-aware) of the current configuration""" + + self._ctx_command = "setElementSequence" + + if not self._configuration_valid: + return + + if not self._ctx_sequence_aware: + return + + self._check(self._doSetElementSequence)(paths) + + def setRule(self, rule): + """Set the current configuration's applicability rule""" + + self._ctx_command = "setRule" + + if not self._configuration_valid: + return + + self._doSetRule(rule) + + def setParameter(self, path, value): + """Set a parameter value for the current configuration""" + + self._ctx_command = "setParameter" + + if not self._configuration_valid: + return + + self._check(self._doSetParameter)(path, value) + + def _handleException(self, exception): + raise exception + + def _notImplemented(self): + raise NotImplementedError( + "{} is an abstract class".format(self.__class__)) + + # Implementation methods + def _doCreateDomain(self, name): + self._notImplemented() + + def _doSetSequenceAware(self): + self._notImplemented() + + def _doAddElement(self, path): + self._notImplemented() + + def _doCreateConfiguration(self, name): + self._notImplemented() + + def _doSetElementSequence(self, paths): + self._notImplemented() + + def _doSetRule(self, rule): + self._notImplemented() + + def _doSetParameter(self, path, value): + self._notImplemented() diff --git a/tools/xmlGenerator/hostDomainGenerator.sh b/tools/xmlGenerator/hostDomainGenerator.sh index c084753..4222382 100755 --- a/tools/xmlGenerator/hostDomainGenerator.sh +++ b/tools/xmlGenerator/hostDomainGenerator.sh @@ -296,7 +296,7 @@ fi # Send the extended domain description routing files converted to pfw commands m4 "$@" | - "$PFWScriptGenerator" --pfw | + "$PFWScriptGenerator" --output-kind pfw | deleteEscapedNewLines | forEachLine "$PFWSendCommand @" | sed '/^Done$/d' diff --git a/tools/xmlGenerator/lightRoutingUpdate.sh b/tools/xmlGenerator/lightRoutingUpdate.sh index 4bde78f..7403622 100755 --- a/tools/xmlGenerator/lightRoutingUpdate.sh +++ b/tools/xmlGenerator/lightRoutingUpdate.sh @@ -100,7 +100,7 @@ done log "Generate domain commands from file(s): $*" m4 "$@" \ - | $(dirname $0)/PFWScriptGenerator.py --pfw >> "${tmpfile}" + | $(dirname $0)/PFWScriptGenerator.py --output-kind pfw >> "${tmpfile}" echo "setAutoSync off" >> "${tmpfile}" diff --git a/tools/xmlGenerator/updateRoutageDomains.sh b/tools/xmlGenerator/updateRoutageDomains.sh index 040e38e..69d57b5 100755 --- a/tools/xmlGenerator/updateRoutageDomains.sh +++ b/tools/xmlGenerator/updateRoutageDomains.sh @@ -74,7 +74,7 @@ function androidWithError () echoColor "Translate domains to pfw commands" echoColor "Domains source file: $DomainFile" -m4 "$DomainFile" | $(dirname $0)/PFWScriptGenerator.py --pfw -o "$scriptPFWFile" +m4 "$DomainFile" | $(dirname $0)/PFWScriptGenerator.py --output-kind pfw -o "$scriptPFWFile" echoColor "List of generated domains :" sed -ne 's/createDomain \(.*\)/ \1/p' "$scriptPFWFile" |