mmtestenv/mmtesttools/Build/buildutils/MbcUtils.py
changeset 0 40261b775718
equal deleted inserted replaced
-1:000000000000 0:40261b775718
       
     1 # Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 # All rights reserved.
       
     3 # This component and the accompanying materials are made available
       
     4 # under the terms of "Eclipse Public License v1.0"
       
     5 # which accompanies this distribution, and is available
       
     6 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 #
       
     8 # Initial Contributors:
       
     9 # Nokia Corporation - initial contribution.
       
    10 #
       
    11 # Contributors:
       
    12 #
       
    13 # Description:
       
    14 # Tools to parse mbc files
       
    15 # NOTE: There is unit etc test code for this in testMbcUtils.py
       
    16 # Any changes to this file should be checked with "python testMbcUtils.py"
       
    17 # Also note that the test code coverage is not 100%
       
    18 #
       
    19 
       
    20 import re
       
    21 import types
       
    22 import os
       
    23 
       
    24 class InvalidInput(Exception):
       
    25     """Raised for invalid data in files"""
       
    26 
       
    27     def __init__(self, badInput):
       
    28         self.__badInput = badInput
       
    29 
       
    30     def __str__(self):
       
    31         return "InvalidInput(%s)" % self.__badInput
       
    32 
       
    33 class Unimplemented(Exception):
       
    34     """Raised for features we don't cover"""
       
    35 
       
    36     def __init__(self, msg):
       
    37         self.__msg = msg
       
    38 
       
    39     def __str__(self):
       
    40         return "Unimplemented(%s)" % self.__msg
       
    41 
       
    42 class InvalidArg(Exception):
       
    43     """Raised for invalid data in argument"""
       
    44 
       
    45     def __init__(self, badArg):
       
    46         self.__badArg = badArg
       
    47 
       
    48     def __str__(self):
       
    49         return "InvalidArg(%s)" % self.__badArg
       
    50 
       
    51 class MissingFile(Exception):
       
    52     "Raised when we expect a file and it does not exist"
       
    53 
       
    54     def __init__(self, badFname):
       
    55         self.__badFname = badFname
       
    56 
       
    57     def __str__(self):
       
    58         return "MissingFile(%s)" % self.__badFname
       
    59 
       
    60 class _MbcParser(object):
       
    61     """Parse an mbc file, given by name. Return as tuple of the given categories, coupled with the filename."""
       
    62 
       
    63     # __slots__ = ["__fname", "__result", "__state"]
       
    64 
       
    65     __nullState = -1
       
    66     __dirsSectionState = 1 # assumed to be same as tuple index
       
    67     __optionDirsSectionState = 2 # assumed to be same as tuple index
       
    68     __commandSectionState = 3 # assumed to be same as tuple index
       
    69 
       
    70     def __init__(self, fname):
       
    71         self.__fname = fname
       
    72         self.__dirname = os.path.dirname(fname)
       
    73         self.__result = None
       
    74         self.__state = _MbcParser.__nullState
       
    75         self.__executed = False
       
    76 
       
    77     def execute(self):
       
    78         "Call to generate the result"
       
    79         self.__reset(self.__fname) # initialize __result etc
       
    80         self.__openAndParse()
       
    81         self.__executed = True
       
    82 
       
    83     def __call__(self):
       
    84         if not self.__executed:
       
    85             self.execute()
       
    86         return self.__result
       
    87 
       
    88     def __reset(self, fname=None):
       
    89         self.__result = (fname,[],[],[])
       
    90 
       
    91     def __openAndParse(self):
       
    92         "Open file and then parse line-by-line"
       
    93         # note deliberately use old version to avoid 2.4 issue. Going foreard, "with" seems to be the correct approach
       
    94         # could perhaps write this better using a parser module, or more extensive use of re, but this is not seen as module specific
       
    95         inputFile = open(self.__fname)
       
    96         try:
       
    97             for line in inputFile:
       
    98                 self.__parseLine(line)
       
    99         finally:
       
   100             inputFile.close()
       
   101 
       
   102     def __parseLine(self,line):
       
   103         # initially we want the bit of the line up to the first // or \n
       
   104         # TODO should be able to do in one re?
       
   105         lookforeoln = re.match(r"(.*)$", line) # lose any \n
       
   106         if lookforeoln:
       
   107             line = lookforeoln.group(1)
       
   108         lookforcomment = re.match(r"(.*)//.*", line)
       
   109         if lookforcomment:
       
   110             line = lookforcomment.group(1)
       
   111         line.strip() # remove spaces at start and end
       
   112         if not line:
       
   113             # skip blank line
       
   114             return;
       
   115         if line == "SECTION_DIRS":
       
   116             self.__state = _MbcParser.__dirsSectionState
       
   117         elif line == "SECTION_OPTIONALDIRS":
       
   118             self.__state = _MbcParser.__optionDirsSectionState
       
   119         elif line == "SECTION_COMMANDS":
       
   120             self.__state = _MbcParser.__commandSectionState
       
   121         else:
       
   122             # for dirs or optionDirs section we are after a single string to treat as directory. don't check here
       
   123             if ((self.__state == _MbcParser.__dirsSectionState) or
       
   124                 (self.__state == _MbcParser.__optionDirsSectionState)):
       
   125                 matchre = re.match(r'[_\-a-z0-9\\/\.]+$', line, re.IGNORECASE)
       
   126                 if matchre:
       
   127                     # we have a match - add to the tuple
       
   128                     matchresult = line # no need to decode. whole thing is the line
       
   129                     if matchresult[0] != "\\":
       
   130                         # relative path - need to add folder name
       
   131                         matchresult = os.path.abspath(os.path.join(self.__dirname, matchresult))
       
   132                     self.__result[self.__state].append(matchresult)
       
   133                 else:
       
   134                     raise InvalidInput (line)
       
   135             elif self.__state == _MbcParser.__commandSectionState:
       
   136                 matchre = re.match(r'oneoff\s+([_\-a-z0-9\\/\.]+)\s+(.+)$', line, re.IGNORECASE)
       
   137                 if matchre:
       
   138                     # append tuple of (directory, command). Comes as (group(1),group(2)) but have to
       
   139                     # convert relative directory we get to absolute if required
       
   140                     matchDir = matchre.group(1)
       
   141                     if matchDir[0] != "\\":
       
   142                         # relative path- need to add folder name
       
   143                         matchDir = os.path.abspath(os.path.join(self.__dirname, matchDir))
       
   144                     self.__result[self.__state].append((matchDir,matchre.group(2)))
       
   145                 else:
       
   146                     raise InvalidInput (line)
       
   147 
       
   148 class _MbcListHandle(object):
       
   149     """Handle a list or tuple of filenames, recursively list of tuples as produced by _MbcParser"""
       
   150 
       
   151     def __init__(self, fnameList):
       
   152         """Assume fnameList is a container. Always generate a list"""
       
   153         self.__fnameList = fnameList
       
   154         self.__result = []
       
   155         self.__executed = False
       
   156 
       
   157     def execute(self):
       
   158         for fname in self.__fnameList:
       
   159             parser = MbcParser(fname)
       
   160             parser.execute()
       
   161             self.__result.append(parser())
       
   162         self.__exectuted = True
       
   163 
       
   164     def __call__(self):
       
   165         if not self.__exectuted:
       
   166             self.execute()
       
   167         return self.__result
       
   168 
       
   169 class MbcParser(object):
       
   170     """Given a list of or a filename, return equivalent structure with each filename replaced by tuple of content
       
   171 
       
   172     tuple elements are:
       
   173     0 - filename
       
   174     1 - main directories
       
   175     2 - optional directories
       
   176     3 - oneoff"""
       
   177 
       
   178     def __init__(self, fnameOrList):
       
   179         self.__fnameOrList = fnameOrList
       
   180         self.__result = None
       
   181         self.__executed = False
       
   182 
       
   183     def execute(self):
       
   184         fnameOrList = self.__fnameOrList
       
   185         if isinstance(fnameOrList, list) or isinstance(fnameOrList, tuple):
       
   186             parser = _MbcListHandle(fnameOrList)
       
   187             parser.execute()
       
   188             self.__result = parser()
       
   189         elif isinstance(fnameOrList, types.StringTypes):
       
   190             parser = _MbcParser(fnameOrList)
       
   191             parser.execute()
       
   192             self.__result = parser()
       
   193         else:
       
   194             raise InvalidArg(fnameOrList)
       
   195 
       
   196     def __call__(self):
       
   197         if not self.__executed:
       
   198             self.execute()
       
   199         return self.__result
       
   200 
       
   201 class GetFolderList(object):
       
   202     """Given output of MbcParser(), produces list of tuples in the format:
       
   203 
       
   204     0 - Element is folder if True (only thing currently supported)
       
   205     1 - folder
       
   206     2 - comment to use in generated file including original filename
       
   207 
       
   208     If folder is optional, will only be added if exists - actually if nameToCheck exists in folder.
       
   209     If folder is not optional, and does not exist, this will raise an error.
       
   210     """
       
   211 
       
   212     def __init__(self, inputList, nameToCheck="bld.inf"):
       
   213         if isinstance(inputList, tuple):
       
   214             # single element "list" from MbcParser is not always a list!
       
   215             self.__list = [inputList]
       
   216         else:
       
   217             self.__list = inputList
       
   218         self.__nameToCheck = nameToCheck
       
   219         self.__result = []
       
   220         self.__exectuted = False
       
   221 
       
   222     def execute(self):
       
   223         self.__result = []
       
   224         self.__genResult()
       
   225         self.__executed = True
       
   226 
       
   227     def __genResult(self):
       
   228         "Process whole list"
       
   229         for listEle in self.__list:
       
   230             self.__processEle(listEle)
       
   231 
       
   232     def __processEle(self, listEle):
       
   233         "Process single element in input list"
       
   234         (fname, dirs, optDirs, commands) = listEle
       
   235         for dir in dirs:
       
   236             combinedFname = os.path.join(dir, self.__nameToCheck)
       
   237             exists = os.path.exists(combinedFname)
       
   238             if not exists:
       
   239                 raise MissingFile(combinedFname)
       
   240             self.__result.append((True, dir, "From %s"%fname))
       
   241         for dir in optDirs:
       
   242             combinedFname = os.path.join(dir, self.__nameToCheck)
       
   243             exists = os.path.exists(combinedFname)
       
   244             if exists:
       
   245                 self.__result.append((True, dir, "From %s"%fname))
       
   246             else:
       
   247                 self.__result.append((True, None, """Skip "%s" from %s"""%(dir,fname)))
       
   248         if commands:
       
   249             raise Unimplemented("No support for oneoff - %s" % str(commands))
       
   250 
       
   251     def __call__(self):
       
   252         if not self.__exectuted:
       
   253             self.execute()
       
   254         return self.__result
       
   255 
       
   256 ## Minimal example configuration file as we have to produce
       
   257 ## See http://developer.symbian.com/wiki/x/rgD6Bg
       
   258 ##
       
   259 ##<SystemDefinition name="BLAH" schema="2.0.0">
       
   260 ##  <systemModel>
       
   261 ##    <layer name="NEW_CUSTOM_LAYER">
       
   262 ##      <collection name="Fake Collection">
       
   263 ##	<component name="examples">
       
   264 ##	  <unit bldFile="C:\Symbian\Carbide2\workspace\hello_whirled\group" />
       
   265 ##	  <unit bldFile="C:\Symbian\Carbide2\workspace\hello_abld\group" />
       
   266 ##	</component>
       
   267 ##      </collection>
       
   268 ##    </layer>
       
   269 ##  </systemModel>
       
   270 ##</SystemDefinition>
       
   271 
       
   272 
       
   273 class ConfigFileGenerator(object):
       
   274     """Generate xml config file given output from GetFolderList
       
   275 
       
   276     Output corresponds to the example in source
       
   277     folderList is input from GetFolderList
       
   278     outputStream is either filename or assumed to be an open file or StringIO"""
       
   279 
       
   280     def __init__(self, folderList, outputStream):
       
   281         self.__folderList = folderList
       
   282         self.__streamParam = outputStream
       
   283         self.__streamIsLocal = False
       
   284         self.__stream = None
       
   285 
       
   286     def __initStream(self):
       
   287         "Work out stream to use. Open stream if required"
       
   288 
       
   289         if isinstance(self.__streamParam, basestring):
       
   290             # unicode or normal string
       
   291             self.__streamIsLocal = True
       
   292             self.__stream = open(self.__streamParam, "w")
       
   293         else:
       
   294             self.__streamIsLocal = False
       
   295             self.__stream = self.__streamParam
       
   296 
       
   297     def __closeStream(self):
       
   298         "If stream is local, close it"
       
   299 
       
   300         if self.__streamIsLocal:
       
   301             self.__stream.close()
       
   302 
       
   303         self.__stream = None # orphan if needs be
       
   304         self.__streamIsLocal = False
       
   305 
       
   306     def write(self):
       
   307         "Called will write output to stream"
       
   308 
       
   309         try:
       
   310             self.__initStream()
       
   311             self.__writeHeaderBit()
       
   312             self.__writeFolderList()
       
   313             self.__writeTailBit()
       
   314         except: # TODO not sure we need this - if we ommit is finally clause run?
       
   315             raise
       
   316         finally:
       
   317             self.__closeStream()
       
   318 
       
   319     def __writeHeaderBit(self):
       
   320         "Write bit of xml before the folder list"
       
   321         # these names all come from the original. Seems no need to vary
       
   322         # this code is 9.5 based
       
   323         print >> self.__stream, r"""<SystemDefinition name="BLAH" schema="2.0.0">"""
       
   324         print >> self.__stream, r"""  <systemModel>"""
       
   325         print >> self.__stream, r"""    <layer name="NEW_CUSTOM_LAYER">"""
       
   326         print >> self.__stream, r"""      <collection name="Fake Collection">"""
       
   327         print >> self.__stream, r"""        <component name="Fake Multimedia">"""
       
   328 
       
   329     def __writeTailBit(self):
       
   330         "write bit of xml after the folder list"
       
   331         print >> self.__stream, r"""        </component>"""
       
   332         print >> self.__stream, r"""      </collection>"""
       
   333         print >> self.__stream, r"""    </layer>"""
       
   334         print >> self.__stream, r"""  </systemModel>"""
       
   335         print >> self.__stream, r"""</SystemDefinition>"""
       
   336 
       
   337     def __writeFolderList(self):
       
   338         for ele in self.__folderList:
       
   339             str = self.__strForListEle(ele)
       
   340             if str:
       
   341                 print >> self.__stream, "          %s" % str
       
   342 
       
   343     def __strForListEle(self, ele):
       
   344         (isFolder, folder, comment) = ele # break down tuple
       
   345         result = None
       
   346         if isFolder:
       
   347             if folder:
       
   348                 result = r"""<unit bldFile="%s" /><!-- %s -->""" % (folder,comment)
       
   349             else:
       
   350                 result = r"""<!-- %s -->""" % (comment)
       
   351         return result
       
   352