mmtestenv/mmtesttools/Build/buildutils/MbcUtils.py
changeset 0 40261b775718
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmtestenv/mmtesttools/Build/buildutils/MbcUtils.py	Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,352 @@
+# Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+# Tools to parse mbc files
+# NOTE: There is unit etc test code for this in testMbcUtils.py
+# Any changes to this file should be checked with "python testMbcUtils.py"
+# Also note that the test code coverage is not 100%
+#
+
+import re
+import types
+import os
+
+class InvalidInput(Exception):
+    """Raised for invalid data in files"""
+
+    def __init__(self, badInput):
+        self.__badInput = badInput
+
+    def __str__(self):
+        return "InvalidInput(%s)" % self.__badInput
+
+class Unimplemented(Exception):
+    """Raised for features we don't cover"""
+
+    def __init__(self, msg):
+        self.__msg = msg
+
+    def __str__(self):
+        return "Unimplemented(%s)" % self.__msg
+
+class InvalidArg(Exception):
+    """Raised for invalid data in argument"""
+
+    def __init__(self, badArg):
+        self.__badArg = badArg
+
+    def __str__(self):
+        return "InvalidArg(%s)" % self.__badArg
+
+class MissingFile(Exception):
+    "Raised when we expect a file and it does not exist"
+
+    def __init__(self, badFname):
+        self.__badFname = badFname
+
+    def __str__(self):
+        return "MissingFile(%s)" % self.__badFname
+
+class _MbcParser(object):
+    """Parse an mbc file, given by name. Return as tuple of the given categories, coupled with the filename."""
+
+    # __slots__ = ["__fname", "__result", "__state"]
+
+    __nullState = -1
+    __dirsSectionState = 1 # assumed to be same as tuple index
+    __optionDirsSectionState = 2 # assumed to be same as tuple index
+    __commandSectionState = 3 # assumed to be same as tuple index
+
+    def __init__(self, fname):
+        self.__fname = fname
+        self.__dirname = os.path.dirname(fname)
+        self.__result = None
+        self.__state = _MbcParser.__nullState
+        self.__executed = False
+
+    def execute(self):
+        "Call to generate the result"
+        self.__reset(self.__fname) # initialize __result etc
+        self.__openAndParse()
+        self.__executed = True
+
+    def __call__(self):
+        if not self.__executed:
+            self.execute()
+        return self.__result
+
+    def __reset(self, fname=None):
+        self.__result = (fname,[],[],[])
+
+    def __openAndParse(self):
+        "Open file and then parse line-by-line"
+        # note deliberately use old version to avoid 2.4 issue. Going foreard, "with" seems to be the correct approach
+        # could perhaps write this better using a parser module, or more extensive use of re, but this is not seen as module specific
+        inputFile = open(self.__fname)
+        try:
+            for line in inputFile:
+                self.__parseLine(line)
+        finally:
+            inputFile.close()
+
+    def __parseLine(self,line):
+        # initially we want the bit of the line up to the first // or \n
+        # TODO should be able to do in one re?
+        lookforeoln = re.match(r"(.*)$", line) # lose any \n
+        if lookforeoln:
+            line = lookforeoln.group(1)
+        lookforcomment = re.match(r"(.*)//.*", line)
+        if lookforcomment:
+            line = lookforcomment.group(1)
+        line.strip() # remove spaces at start and end
+        if not line:
+            # skip blank line
+            return;
+        if line == "SECTION_DIRS":
+            self.__state = _MbcParser.__dirsSectionState
+        elif line == "SECTION_OPTIONALDIRS":
+            self.__state = _MbcParser.__optionDirsSectionState
+        elif line == "SECTION_COMMANDS":
+            self.__state = _MbcParser.__commandSectionState
+        else:
+            # for dirs or optionDirs section we are after a single string to treat as directory. don't check here
+            if ((self.__state == _MbcParser.__dirsSectionState) or
+                (self.__state == _MbcParser.__optionDirsSectionState)):
+                matchre = re.match(r'[_\-a-z0-9\\/\.]+$', line, re.IGNORECASE)
+                if matchre:
+                    # we have a match - add to the tuple
+                    matchresult = line # no need to decode. whole thing is the line
+                    if matchresult[0] != "\\":
+                        # relative path - need to add folder name
+                        matchresult = os.path.abspath(os.path.join(self.__dirname, matchresult))
+                    self.__result[self.__state].append(matchresult)
+                else:
+                    raise InvalidInput (line)
+            elif self.__state == _MbcParser.__commandSectionState:
+                matchre = re.match(r'oneoff\s+([_\-a-z0-9\\/\.]+)\s+(.+)$', line, re.IGNORECASE)
+                if matchre:
+                    # append tuple of (directory, command). Comes as (group(1),group(2)) but have to
+                    # convert relative directory we get to absolute if required
+                    matchDir = matchre.group(1)
+                    if matchDir[0] != "\\":
+                        # relative path- need to add folder name
+                        matchDir = os.path.abspath(os.path.join(self.__dirname, matchDir))
+                    self.__result[self.__state].append((matchDir,matchre.group(2)))
+                else:
+                    raise InvalidInput (line)
+
+class _MbcListHandle(object):
+    """Handle a list or tuple of filenames, recursively list of tuples as produced by _MbcParser"""
+
+    def __init__(self, fnameList):
+        """Assume fnameList is a container. Always generate a list"""
+        self.__fnameList = fnameList
+        self.__result = []
+        self.__executed = False
+
+    def execute(self):
+        for fname in self.__fnameList:
+            parser = MbcParser(fname)
+            parser.execute()
+            self.__result.append(parser())
+        self.__exectuted = True
+
+    def __call__(self):
+        if not self.__exectuted:
+            self.execute()
+        return self.__result
+
+class MbcParser(object):
+    """Given a list of or a filename, return equivalent structure with each filename replaced by tuple of content
+
+    tuple elements are:
+    0 - filename
+    1 - main directories
+    2 - optional directories
+    3 - oneoff"""
+
+    def __init__(self, fnameOrList):
+        self.__fnameOrList = fnameOrList
+        self.__result = None
+        self.__executed = False
+
+    def execute(self):
+        fnameOrList = self.__fnameOrList
+        if isinstance(fnameOrList, list) or isinstance(fnameOrList, tuple):
+            parser = _MbcListHandle(fnameOrList)
+            parser.execute()
+            self.__result = parser()
+        elif isinstance(fnameOrList, types.StringTypes):
+            parser = _MbcParser(fnameOrList)
+            parser.execute()
+            self.__result = parser()
+        else:
+            raise InvalidArg(fnameOrList)
+
+    def __call__(self):
+        if not self.__executed:
+            self.execute()
+        return self.__result
+
+class GetFolderList(object):
+    """Given output of MbcParser(), produces list of tuples in the format:
+
+    0 - Element is folder if True (only thing currently supported)
+    1 - folder
+    2 - comment to use in generated file including original filename
+
+    If folder is optional, will only be added if exists - actually if nameToCheck exists in folder.
+    If folder is not optional, and does not exist, this will raise an error.
+    """
+
+    def __init__(self, inputList, nameToCheck="bld.inf"):
+        if isinstance(inputList, tuple):
+            # single element "list" from MbcParser is not always a list!
+            self.__list = [inputList]
+        else:
+            self.__list = inputList
+        self.__nameToCheck = nameToCheck
+        self.__result = []
+        self.__exectuted = False
+
+    def execute(self):
+        self.__result = []
+        self.__genResult()
+        self.__executed = True
+
+    def __genResult(self):
+        "Process whole list"
+        for listEle in self.__list:
+            self.__processEle(listEle)
+
+    def __processEle(self, listEle):
+        "Process single element in input list"
+        (fname, dirs, optDirs, commands) = listEle
+        for dir in dirs:
+            combinedFname = os.path.join(dir, self.__nameToCheck)
+            exists = os.path.exists(combinedFname)
+            if not exists:
+                raise MissingFile(combinedFname)
+            self.__result.append((True, dir, "From %s"%fname))
+        for dir in optDirs:
+            combinedFname = os.path.join(dir, self.__nameToCheck)
+            exists = os.path.exists(combinedFname)
+            if exists:
+                self.__result.append((True, dir, "From %s"%fname))
+            else:
+                self.__result.append((True, None, """Skip "%s" from %s"""%(dir,fname)))
+        if commands:
+            raise Unimplemented("No support for oneoff - %s" % str(commands))
+
+    def __call__(self):
+        if not self.__exectuted:
+            self.execute()
+        return self.__result
+
+## Minimal example configuration file as we have to produce
+## See http://developer.symbian.com/wiki/x/rgD6Bg
+##
+##<SystemDefinition name="BLAH" schema="2.0.0">
+##  <systemModel>
+##    <layer name="NEW_CUSTOM_LAYER">
+##      <collection name="Fake Collection">
+##	<component name="examples">
+##	  <unit bldFile="C:\Symbian\Carbide2\workspace\hello_whirled\group" />
+##	  <unit bldFile="C:\Symbian\Carbide2\workspace\hello_abld\group" />
+##	</component>
+##      </collection>
+##    </layer>
+##  </systemModel>
+##</SystemDefinition>
+
+
+class ConfigFileGenerator(object):
+    """Generate xml config file given output from GetFolderList
+
+    Output corresponds to the example in source
+    folderList is input from GetFolderList
+    outputStream is either filename or assumed to be an open file or StringIO"""
+
+    def __init__(self, folderList, outputStream):
+        self.__folderList = folderList
+        self.__streamParam = outputStream
+        self.__streamIsLocal = False
+        self.__stream = None
+
+    def __initStream(self):
+        "Work out stream to use. Open stream if required"
+
+        if isinstance(self.__streamParam, basestring):
+            # unicode or normal string
+            self.__streamIsLocal = True
+            self.__stream = open(self.__streamParam, "w")
+        else:
+            self.__streamIsLocal = False
+            self.__stream = self.__streamParam
+
+    def __closeStream(self):
+        "If stream is local, close it"
+
+        if self.__streamIsLocal:
+            self.__stream.close()
+
+        self.__stream = None # orphan if needs be
+        self.__streamIsLocal = False
+
+    def write(self):
+        "Called will write output to stream"
+
+        try:
+            self.__initStream()
+            self.__writeHeaderBit()
+            self.__writeFolderList()
+            self.__writeTailBit()
+        except: # TODO not sure we need this - if we ommit is finally clause run?
+            raise
+        finally:
+            self.__closeStream()
+
+    def __writeHeaderBit(self):
+        "Write bit of xml before the folder list"
+        # these names all come from the original. Seems no need to vary
+        # this code is 9.5 based
+        print >> self.__stream, r"""<SystemDefinition name="BLAH" schema="2.0.0">"""
+        print >> self.__stream, r"""  <systemModel>"""
+        print >> self.__stream, r"""    <layer name="NEW_CUSTOM_LAYER">"""
+        print >> self.__stream, r"""      <collection name="Fake Collection">"""
+        print >> self.__stream, r"""        <component name="Fake Multimedia">"""
+
+    def __writeTailBit(self):
+        "write bit of xml after the folder list"
+        print >> self.__stream, r"""        </component>"""
+        print >> self.__stream, r"""      </collection>"""
+        print >> self.__stream, r"""    </layer>"""
+        print >> self.__stream, r"""  </systemModel>"""
+        print >> self.__stream, r"""</SystemDefinition>"""
+
+    def __writeFolderList(self):
+        for ele in self.__folderList:
+            str = self.__strForListEle(ele)
+            if str:
+                print >> self.__stream, "          %s" % str
+
+    def __strForListEle(self, ele):
+        (isFolder, folder, comment) = ele # break down tuple
+        result = None
+        if isFolder:
+            if folder:
+                result = r"""<unit bldFile="%s" /><!-- %s -->""" % (folder,comment)
+            else:
+                result = r"""<!-- %s -->""" % (comment)
+        return result
+