1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmtestenv/mmtesttools/Build/buildutils/MbcUtils.py Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,352 @@
1.4 +# Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +# All rights reserved.
1.6 +# This component and the accompanying materials are made available
1.7 +# under the terms of "Eclipse Public License v1.0"
1.8 +# which accompanies this distribution, and is available
1.9 +# at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +#
1.11 +# Initial Contributors:
1.12 +# Nokia Corporation - initial contribution.
1.13 +#
1.14 +# Contributors:
1.15 +#
1.16 +# Description:
1.17 +# Tools to parse mbc files
1.18 +# NOTE: There is unit etc test code for this in testMbcUtils.py
1.19 +# Any changes to this file should be checked with "python testMbcUtils.py"
1.20 +# Also note that the test code coverage is not 100%
1.21 +#
1.22 +
1.23 +import re
1.24 +import types
1.25 +import os
1.26 +
1.27 +class InvalidInput(Exception):
1.28 + """Raised for invalid data in files"""
1.29 +
1.30 + def __init__(self, badInput):
1.31 + self.__badInput = badInput
1.32 +
1.33 + def __str__(self):
1.34 + return "InvalidInput(%s)" % self.__badInput
1.35 +
1.36 +class Unimplemented(Exception):
1.37 + """Raised for features we don't cover"""
1.38 +
1.39 + def __init__(self, msg):
1.40 + self.__msg = msg
1.41 +
1.42 + def __str__(self):
1.43 + return "Unimplemented(%s)" % self.__msg
1.44 +
1.45 +class InvalidArg(Exception):
1.46 + """Raised for invalid data in argument"""
1.47 +
1.48 + def __init__(self, badArg):
1.49 + self.__badArg = badArg
1.50 +
1.51 + def __str__(self):
1.52 + return "InvalidArg(%s)" % self.__badArg
1.53 +
1.54 +class MissingFile(Exception):
1.55 + "Raised when we expect a file and it does not exist"
1.56 +
1.57 + def __init__(self, badFname):
1.58 + self.__badFname = badFname
1.59 +
1.60 + def __str__(self):
1.61 + return "MissingFile(%s)" % self.__badFname
1.62 +
1.63 +class _MbcParser(object):
1.64 + """Parse an mbc file, given by name. Return as tuple of the given categories, coupled with the filename."""
1.65 +
1.66 + # __slots__ = ["__fname", "__result", "__state"]
1.67 +
1.68 + __nullState = -1
1.69 + __dirsSectionState = 1 # assumed to be same as tuple index
1.70 + __optionDirsSectionState = 2 # assumed to be same as tuple index
1.71 + __commandSectionState = 3 # assumed to be same as tuple index
1.72 +
1.73 + def __init__(self, fname):
1.74 + self.__fname = fname
1.75 + self.__dirname = os.path.dirname(fname)
1.76 + self.__result = None
1.77 + self.__state = _MbcParser.__nullState
1.78 + self.__executed = False
1.79 +
1.80 + def execute(self):
1.81 + "Call to generate the result"
1.82 + self.__reset(self.__fname) # initialize __result etc
1.83 + self.__openAndParse()
1.84 + self.__executed = True
1.85 +
1.86 + def __call__(self):
1.87 + if not self.__executed:
1.88 + self.execute()
1.89 + return self.__result
1.90 +
1.91 + def __reset(self, fname=None):
1.92 + self.__result = (fname,[],[],[])
1.93 +
1.94 + def __openAndParse(self):
1.95 + "Open file and then parse line-by-line"
1.96 + # note deliberately use old version to avoid 2.4 issue. Going foreard, "with" seems to be the correct approach
1.97 + # could perhaps write this better using a parser module, or more extensive use of re, but this is not seen as module specific
1.98 + inputFile = open(self.__fname)
1.99 + try:
1.100 + for line in inputFile:
1.101 + self.__parseLine(line)
1.102 + finally:
1.103 + inputFile.close()
1.104 +
1.105 + def __parseLine(self,line):
1.106 + # initially we want the bit of the line up to the first // or \n
1.107 + # TODO should be able to do in one re?
1.108 + lookforeoln = re.match(r"(.*)$", line) # lose any \n
1.109 + if lookforeoln:
1.110 + line = lookforeoln.group(1)
1.111 + lookforcomment = re.match(r"(.*)//.*", line)
1.112 + if lookforcomment:
1.113 + line = lookforcomment.group(1)
1.114 + line.strip() # remove spaces at start and end
1.115 + if not line:
1.116 + # skip blank line
1.117 + return;
1.118 + if line == "SECTION_DIRS":
1.119 + self.__state = _MbcParser.__dirsSectionState
1.120 + elif line == "SECTION_OPTIONALDIRS":
1.121 + self.__state = _MbcParser.__optionDirsSectionState
1.122 + elif line == "SECTION_COMMANDS":
1.123 + self.__state = _MbcParser.__commandSectionState
1.124 + else:
1.125 + # for dirs or optionDirs section we are after a single string to treat as directory. don't check here
1.126 + if ((self.__state == _MbcParser.__dirsSectionState) or
1.127 + (self.__state == _MbcParser.__optionDirsSectionState)):
1.128 + matchre = re.match(r'[_\-a-z0-9\\/\.]+$', line, re.IGNORECASE)
1.129 + if matchre:
1.130 + # we have a match - add to the tuple
1.131 + matchresult = line # no need to decode. whole thing is the line
1.132 + if matchresult[0] != "\\":
1.133 + # relative path - need to add folder name
1.134 + matchresult = os.path.abspath(os.path.join(self.__dirname, matchresult))
1.135 + self.__result[self.__state].append(matchresult)
1.136 + else:
1.137 + raise InvalidInput (line)
1.138 + elif self.__state == _MbcParser.__commandSectionState:
1.139 + matchre = re.match(r'oneoff\s+([_\-a-z0-9\\/\.]+)\s+(.+)$', line, re.IGNORECASE)
1.140 + if matchre:
1.141 + # append tuple of (directory, command). Comes as (group(1),group(2)) but have to
1.142 + # convert relative directory we get to absolute if required
1.143 + matchDir = matchre.group(1)
1.144 + if matchDir[0] != "\\":
1.145 + # relative path- need to add folder name
1.146 + matchDir = os.path.abspath(os.path.join(self.__dirname, matchDir))
1.147 + self.__result[self.__state].append((matchDir,matchre.group(2)))
1.148 + else:
1.149 + raise InvalidInput (line)
1.150 +
1.151 +class _MbcListHandle(object):
1.152 + """Handle a list or tuple of filenames, recursively list of tuples as produced by _MbcParser"""
1.153 +
1.154 + def __init__(self, fnameList):
1.155 + """Assume fnameList is a container. Always generate a list"""
1.156 + self.__fnameList = fnameList
1.157 + self.__result = []
1.158 + self.__executed = False
1.159 +
1.160 + def execute(self):
1.161 + for fname in self.__fnameList:
1.162 + parser = MbcParser(fname)
1.163 + parser.execute()
1.164 + self.__result.append(parser())
1.165 + self.__exectuted = True
1.166 +
1.167 + def __call__(self):
1.168 + if not self.__exectuted:
1.169 + self.execute()
1.170 + return self.__result
1.171 +
1.172 +class MbcParser(object):
1.173 + """Given a list of or a filename, return equivalent structure with each filename replaced by tuple of content
1.174 +
1.175 + tuple elements are:
1.176 + 0 - filename
1.177 + 1 - main directories
1.178 + 2 - optional directories
1.179 + 3 - oneoff"""
1.180 +
1.181 + def __init__(self, fnameOrList):
1.182 + self.__fnameOrList = fnameOrList
1.183 + self.__result = None
1.184 + self.__executed = False
1.185 +
1.186 + def execute(self):
1.187 + fnameOrList = self.__fnameOrList
1.188 + if isinstance(fnameOrList, list) or isinstance(fnameOrList, tuple):
1.189 + parser = _MbcListHandle(fnameOrList)
1.190 + parser.execute()
1.191 + self.__result = parser()
1.192 + elif isinstance(fnameOrList, types.StringTypes):
1.193 + parser = _MbcParser(fnameOrList)
1.194 + parser.execute()
1.195 + self.__result = parser()
1.196 + else:
1.197 + raise InvalidArg(fnameOrList)
1.198 +
1.199 + def __call__(self):
1.200 + if not self.__executed:
1.201 + self.execute()
1.202 + return self.__result
1.203 +
1.204 +class GetFolderList(object):
1.205 + """Given output of MbcParser(), produces list of tuples in the format:
1.206 +
1.207 + 0 - Element is folder if True (only thing currently supported)
1.208 + 1 - folder
1.209 + 2 - comment to use in generated file including original filename
1.210 +
1.211 + If folder is optional, will only be added if exists - actually if nameToCheck exists in folder.
1.212 + If folder is not optional, and does not exist, this will raise an error.
1.213 + """
1.214 +
1.215 + def __init__(self, inputList, nameToCheck="bld.inf"):
1.216 + if isinstance(inputList, tuple):
1.217 + # single element "list" from MbcParser is not always a list!
1.218 + self.__list = [inputList]
1.219 + else:
1.220 + self.__list = inputList
1.221 + self.__nameToCheck = nameToCheck
1.222 + self.__result = []
1.223 + self.__exectuted = False
1.224 +
1.225 + def execute(self):
1.226 + self.__result = []
1.227 + self.__genResult()
1.228 + self.__executed = True
1.229 +
1.230 + def __genResult(self):
1.231 + "Process whole list"
1.232 + for listEle in self.__list:
1.233 + self.__processEle(listEle)
1.234 +
1.235 + def __processEle(self, listEle):
1.236 + "Process single element in input list"
1.237 + (fname, dirs, optDirs, commands) = listEle
1.238 + for dir in dirs:
1.239 + combinedFname = os.path.join(dir, self.__nameToCheck)
1.240 + exists = os.path.exists(combinedFname)
1.241 + if not exists:
1.242 + raise MissingFile(combinedFname)
1.243 + self.__result.append((True, dir, "From %s"%fname))
1.244 + for dir in optDirs:
1.245 + combinedFname = os.path.join(dir, self.__nameToCheck)
1.246 + exists = os.path.exists(combinedFname)
1.247 + if exists:
1.248 + self.__result.append((True, dir, "From %s"%fname))
1.249 + else:
1.250 + self.__result.append((True, None, """Skip "%s" from %s"""%(dir,fname)))
1.251 + if commands:
1.252 + raise Unimplemented("No support for oneoff - %s" % str(commands))
1.253 +
1.254 + def __call__(self):
1.255 + if not self.__exectuted:
1.256 + self.execute()
1.257 + return self.__result
1.258 +
1.259 +## Minimal example configuration file as we have to produce
1.260 +## See http://developer.symbian.com/wiki/x/rgD6Bg
1.261 +##
1.262 +##<SystemDefinition name="BLAH" schema="2.0.0">
1.263 +## <systemModel>
1.264 +## <layer name="NEW_CUSTOM_LAYER">
1.265 +## <collection name="Fake Collection">
1.266 +## <component name="examples">
1.267 +## <unit bldFile="C:\Symbian\Carbide2\workspace\hello_whirled\group" />
1.268 +## <unit bldFile="C:\Symbian\Carbide2\workspace\hello_abld\group" />
1.269 +## </component>
1.270 +## </collection>
1.271 +## </layer>
1.272 +## </systemModel>
1.273 +##</SystemDefinition>
1.274 +
1.275 +
1.276 +class ConfigFileGenerator(object):
1.277 + """Generate xml config file given output from GetFolderList
1.278 +
1.279 + Output corresponds to the example in source
1.280 + folderList is input from GetFolderList
1.281 + outputStream is either filename or assumed to be an open file or StringIO"""
1.282 +
1.283 + def __init__(self, folderList, outputStream):
1.284 + self.__folderList = folderList
1.285 + self.__streamParam = outputStream
1.286 + self.__streamIsLocal = False
1.287 + self.__stream = None
1.288 +
1.289 + def __initStream(self):
1.290 + "Work out stream to use. Open stream if required"
1.291 +
1.292 + if isinstance(self.__streamParam, basestring):
1.293 + # unicode or normal string
1.294 + self.__streamIsLocal = True
1.295 + self.__stream = open(self.__streamParam, "w")
1.296 + else:
1.297 + self.__streamIsLocal = False
1.298 + self.__stream = self.__streamParam
1.299 +
1.300 + def __closeStream(self):
1.301 + "If stream is local, close it"
1.302 +
1.303 + if self.__streamIsLocal:
1.304 + self.__stream.close()
1.305 +
1.306 + self.__stream = None # orphan if needs be
1.307 + self.__streamIsLocal = False
1.308 +
1.309 + def write(self):
1.310 + "Called will write output to stream"
1.311 +
1.312 + try:
1.313 + self.__initStream()
1.314 + self.__writeHeaderBit()
1.315 + self.__writeFolderList()
1.316 + self.__writeTailBit()
1.317 + except: # TODO not sure we need this - if we ommit is finally clause run?
1.318 + raise
1.319 + finally:
1.320 + self.__closeStream()
1.321 +
1.322 + def __writeHeaderBit(self):
1.323 + "Write bit of xml before the folder list"
1.324 + # these names all come from the original. Seems no need to vary
1.325 + # this code is 9.5 based
1.326 + print >> self.__stream, r"""<SystemDefinition name="BLAH" schema="2.0.0">"""
1.327 + print >> self.__stream, r""" <systemModel>"""
1.328 + print >> self.__stream, r""" <layer name="NEW_CUSTOM_LAYER">"""
1.329 + print >> self.__stream, r""" <collection name="Fake Collection">"""
1.330 + print >> self.__stream, r""" <component name="Fake Multimedia">"""
1.331 +
1.332 + def __writeTailBit(self):
1.333 + "write bit of xml after the folder list"
1.334 + print >> self.__stream, r""" </component>"""
1.335 + print >> self.__stream, r""" </collection>"""
1.336 + print >> self.__stream, r""" </layer>"""
1.337 + print >> self.__stream, r""" </systemModel>"""
1.338 + print >> self.__stream, r"""</SystemDefinition>"""
1.339 +
1.340 + def __writeFolderList(self):
1.341 + for ele in self.__folderList:
1.342 + str = self.__strForListEle(ele)
1.343 + if str:
1.344 + print >> self.__stream, " %s" % str
1.345 +
1.346 + def __strForListEle(self, ele):
1.347 + (isFolder, folder, comment) = ele # break down tuple
1.348 + result = None
1.349 + if isFolder:
1.350 + if folder:
1.351 + result = r"""<unit bldFile="%s" /><!-- %s -->""" % (folder,comment)
1.352 + else:
1.353 + result = r"""<!-- %s -->""" % (comment)
1.354 + return result
1.355 +