sbsv2cache.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #
  2. # Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
  3. # All rights reserved.
  4. # This component and the accompanying materials are made available
  5. # under the terms of the License "Eclipse Public License v1.0"
  6. # which accompanies this distribution, and is available
  7. # at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. #
  9. # Initial Contributors:
  10. # Nokia Corporation - initial contribution.
  11. #
  12. # Contributors:
  13. #
  14. # Description:
  15. # Creates CBR tool compatible cache files from SBSv2 .whatlog variant output
  16. #
  17. import sys
  18. import os
  19. from optparse import OptionParser
  20. import xml.parsers.expat
  21. import re
  22. # Global dictionary of ComponentReleasable objects, keyed on bld.inf file
  23. BuildReleasables = {}
  24. # Provide a means to form "traditional" ABLD-like build platforms and variants from SBSv2 configurations
  25. ConfigMatch = re.compile(r'^(?P<PLATFORM>\w+)_(?P<VARIANT>\w+)(\.((?P<PLATFORMADD>smp)|\w+))*')
  26. WinscwTreeMatch = re.compile(r'[\\|\/]epoc32[\\|\/]release[\\|\/]winscw[\\|\/](?P<VARIANT>(urel|udeb))[\\|\/]', re.IGNORECASE)
  27. WinDriveMatch = re.compile(r'[A-Za-z]:')
  28. # $self->{abldcache}->{'<bld.inf location> export -what'} =
  29. # $self->{abldcache}->{'<bld.inf location> <phase> <platform> <variant> -what'} =
  30. # $self->{abldcache}->{'plats'} =
  31. CacheGroupPrefix = "$self->{abldcache}->{\'"
  32. CacheGroupSuffix = "\'} =\n"
  33. CacheExportGroup = CacheGroupPrefix+"%s export -what"+CacheGroupSuffix
  34. CacheBuildOutputGroup = CacheGroupPrefix+"%s %s %s %s -what"+CacheGroupSuffix
  35. CachePlatsGroup = CacheGroupPrefix+"plats"+CacheGroupSuffix
  36. CacheListOpen = "\t[\n"
  37. CacheListItem = "\t\'%s\'"
  38. CacheListItemPair = "\t[\'%s\', \'%s\']"
  39. CacheListClose = "\t];\n\n"
  40. class ComponentReleasable(object):
  41. """Wraps up a bld.inf file in terms of its packagable releasable output."""
  42. # If EPOCROOT is set, provide a means to confirm that potentially publishable releasables live under EPOCROOT/epoc32
  43. ReleaseTreeMatch = None
  44. if os.environ.has_key("EPOCROOT"):
  45. ReleaseTreeMatch = re.compile(r'\"*'+os.path.abspath(os.path.join(os.environ["EPOCROOT"],"epoc32")).replace('\\',r'\/').replace('\/',r'[\\|\/]+')+r'[\\|\/]+', re.IGNORECASE)
  46. def __init__(self, aBldInfFile, aVerbose=False):
  47. self.__BldInfFile = aBldInfFile
  48. self.__Verbose = aVerbose
  49. self.__Exports = {}
  50. self.__BuildOutput = {}
  51. self.__Platforms = {}
  52. def __IsReleasableItem(self, aBuildItem):
  53. if self.ReleaseTreeMatch and self.ReleaseTreeMatch.match(aBuildItem):
  54. return True
  55. if self.__Verbose:
  56. print "Discarding: \'%s\' from \'%s\' as not in the release tree." % (aBuildItem, self.__BldInfFile)
  57. return False
  58. def __StoreBuildItem(self, aPlatform, aVariant, aBuildItem):
  59. if not self.__BuildOutput.has_key(aPlatform):
  60. self.__BuildOutput[aPlatform] = {}
  61. if aPlatform != "ALL":
  62. self.__Platforms[aPlatform.upper()] = 1
  63. if not self.__BuildOutput[aPlatform].has_key(aVariant):
  64. self.__BuildOutput[aPlatform][aVariant] = {}
  65. if aBuildItem:
  66. self.__BuildOutput[aPlatform][aVariant][aBuildItem] = 1
  67. def AddExport(self, aDestination, aSource):
  68. if not self.__IsReleasableItem(aDestination):
  69. return
  70. self.__Exports[aDestination] = aSource
  71. def AddBuildOutput(self, aBuildItem, aPlatform="ALL", aVariant="ALL"):
  72. if not self.__IsReleasableItem(aBuildItem):
  73. return
  74. if aPlatform != "ALL" and aVariant == "ALL":
  75. self.__StoreBuildItem(aPlatform, "urel", aBuildItem)
  76. self.__StoreBuildItem(aPlatform, "udeb", aBuildItem)
  77. else:
  78. self.__StoreBuildItem(aPlatform, aVariant, aBuildItem)
  79. def Finalise(self):
  80. # Re-visit the stored build items and, in the context of all build platforms having been processed for the
  81. # component, copy platform-generic "ALL" output to the concrete build platform outputs
  82. if self.__BuildOutput.has_key("ALL"):
  83. allItems = self.__BuildOutput["ALL"]["ALL"].keys()
  84. for platform in self.__BuildOutput.keys():
  85. for variant in self.__BuildOutput[platform].keys():
  86. for allItem in allItems:
  87. self.__StoreBuildItem(platform, variant, allItem)
  88. del self.__BuildOutput["ALL"]
  89. def GetBldInf(self):
  90. return self.__BldInfFile
  91. def GetExports(self):
  92. return self.__Exports
  93. def GetBuildOutput(self):
  94. return self.__BuildOutput
  95. def GetPlatforms(self):
  96. return self.__Platforms
  97. def HasReleasables(self):
  98. return (self.__BuildOutput or self.__Exports)
  99. def error(aMessage):
  100. sys.stderr.write("ERROR: sbsv2cache.py : %s\n" % aMessage)
  101. sys.exit(1)
  102. def processReleasableElement(aContext, aName, aValue, aVerbose):
  103. bldinf = aContext["bldinf"]
  104. mmp = aContext["mmp"]
  105. config = aContext["config"]
  106. platform = ""
  107. variant = ""
  108. configMatchResults = ConfigMatch.match(config)
  109. if configMatchResults:
  110. platform = configMatchResults.group('PLATFORM')
  111. variant = configMatchResults.group('VARIANT')
  112. if configMatchResults.group('PLATFORMADD'):
  113. platform += configMatchResults.group('PLATFORMADD')
  114. if not BuildReleasables.has_key(bldinf):
  115. BuildReleasables[bldinf] = ComponentReleasable(bldinf, aVerbose)
  116. componentReleasable = BuildReleasables[bldinf]
  117. if aName == "export" :
  118. componentReleasable.AddExport(aValue["destination"], aValue["source"])
  119. elif aName == "member":
  120. componentReleasable.AddExport(aValue.keys()[0], aContext["zipfile"])
  121. elif aName == "build":
  122. componentReleasable.AddBuildOutput(aValue.keys()[0], platform, variant)
  123. elif aName == "resource" or aName == "bitmap":
  124. item = aValue.keys()[0]
  125. # Identify winscw urel/udeb specific resources, and store accordingly
  126. winscwTreeMatchResult = WinscwTreeMatch.search(item)
  127. if platform == "winscw" and winscwTreeMatchResult:
  128. componentReleasable.AddBuildOutput(item, platform, winscwTreeMatchResult.group("VARIANT").lower())
  129. else:
  130. componentReleasable.AddBuildOutput(item, platform)
  131. elif aName == "stringtable":
  132. componentReleasable.AddBuildOutput(aValue.keys()[0])
  133. def parseLog(aLog, aVerbose):
  134. if not os.path.exists(aLog):
  135. error("Log file %s does not exist." % aLog)
  136. parser = xml.parsers.expat.ParserCreate()
  137. parser.buffer_text = True
  138. elementContext = {}
  139. currentElement = []
  140. def start_element(name, attributes):
  141. if name == "whatlog" or name == "archive":
  142. elementContext.update(attributes)
  143. elif elementContext.has_key("bldinf"):
  144. if name == "export":
  145. # Exports are all attributes, so deal with them directly
  146. processReleasableElement(elementContext, name, attributes, aVerbose)
  147. else:
  148. # Other elements wrap values, get these later
  149. currentElement.append(name)
  150. def end_element(name):
  151. if name == "whatlog":
  152. elementContext.clear()
  153. elif name == "archive":
  154. del elementContext["zipfile"]
  155. def char_data(data):
  156. if elementContext.has_key("bldinf") and currentElement:
  157. processReleasableElement(elementContext, currentElement.pop(), {str(data):1}, aVerbose)
  158. parser.StartElementHandler = start_element
  159. parser.EndElementHandler = end_element
  160. parser.CharacterDataHandler = char_data
  161. try:
  162. if aVerbose:
  163. print "Parsing: " + aLog
  164. parser.ParseFile(open(aLog, "r"))
  165. except xml.parsers.expat.ExpatError, e:
  166. error("Failure parsing log file \'%s\' (line %s)" % (aLog, e.lineno))
  167. def normFileForCache(aFile):
  168. normedFile = WinDriveMatch.sub("",aFile)
  169. normedFile = normedFile.replace("/", "\\")
  170. normedFile = normedFile.replace("\\", "\\\\")
  171. normedFile = normedFile.replace("\\\\\\\\", "\\\\")
  172. normedFile = normedFile.replace("\"", "")
  173. return normedFile
  174. def dumpCacheFileList(aCacheFileObject, aItems, aPairs=False):
  175. numItems = len(aItems)
  176. suffix = ",\n"
  177. aCacheFileObject.write(CacheListOpen)
  178. for item in aItems:
  179. if aItems.index(item) == numItems-1:
  180. suffix = "\n"
  181. if aPairs:
  182. aCacheFileObject.write((CacheListItemPair % (normFileForCache(item[0]), normFileForCache(item[1]))) + suffix)
  183. else:
  184. aCacheFileObject.write((CacheListItem % normFileForCache(item)) + suffix)
  185. aCacheFileObject.write(CacheListClose)
  186. def createCacheFile(aComponentReleasable, aOutputPath, aSourceExports, aVerbose):
  187. if not aComponentReleasable.HasReleasables():
  188. return
  189. cacheFileDir = os.path.normpath(\
  190. os.path.join(aOutputPath, \
  191. WinDriveMatch.sub("",os.path.dirname(aComponentReleasable.GetBldInf())).lstrip(r'/').lstrip(r'\\')))
  192. cacheFile = os.path.join(cacheFileDir, "cache")
  193. bldInfLoc = WinDriveMatch.sub("",os.path.dirname(aComponentReleasable.GetBldInf())).replace("/", "\\")
  194. if aVerbose:
  195. print "Creating: " + cacheFile
  196. if not os.path.exists(cacheFileDir):
  197. os.makedirs(cacheFileDir)
  198. try:
  199. cacheFileObject = open(cacheFile, 'w')
  200. exports = aComponentReleasable.GetExports()
  201. if exports:
  202. cacheFileObject.write(CacheExportGroup % bldInfLoc)
  203. if aSourceExports:
  204. dumpCacheFileList(cacheFileObject, exports.items(), True)
  205. else:
  206. dumpCacheFileList(cacheFileObject, exports.keys())
  207. buildOutput = aComponentReleasable.GetBuildOutput()
  208. if buildOutput:
  209. for plat in buildOutput.keys():
  210. # Most cache output is represented as if performed for the "abld target" phase, but tools platforms
  211. # are presented as if performed by "abld build", and so must additionally replicate any exports
  212. # performed for the component in their variant output
  213. phase = "target"
  214. additionalOutput = []
  215. if plat == "tools" or plat == "tools2":
  216. phase = "build"
  217. if exports:
  218. additionalOutput = exports.keys()
  219. for variant in buildOutput[plat].keys():
  220. cacheFileObject.write(CacheBuildOutputGroup % (bldInfLoc, phase, plat, variant))
  221. dumpCacheFileList(cacheFileObject, buildOutput[plat][variant].keys() + additionalOutput)
  222. cacheFileObject.write(CachePlatsGroup)
  223. dumpCacheFileList(cacheFileObject, aComponentReleasable.GetPlatforms().keys())
  224. cacheFileObject.close()
  225. except IOError:
  226. error("Failure creating cache file %s." % cacheFile)
  227. def main():
  228. parser = OptionParser(prog="sbsv2cache.py")
  229. parser.add_option("-l", "--log", action="append", dest="logs", help="log file to parse for <whatlog/> wrapped content.")
  230. parser.add_option("-o", "--outputpath", action="store", dest="outputpath", help="root location to generate cache files.")
  231. parser.add_option("-s", "--sourceexports", action="store_true", default=False, dest="sourceexports", help="generate cache files where each element in the export array is a ['destination', 'source'] array rather than just a 'destination' element.")
  232. parser.add_option("-v", "--verbose", action="store_true", default=False, dest="verbose", help="provide more information as things happen.")
  233. (options, leftover_args) = parser.parse_args(sys.argv[1:])
  234. if leftover_args or not options.logs or not options.outputpath:
  235. parser.print_help()
  236. sys.exit(1)
  237. print "sbsv2cache: started"
  238. # Parse build logs to populate the BuildReleasables dictionary
  239. for log in options.logs:
  240. parseLog(os.path.abspath(log), options.verbose)
  241. # Finalise components in BuildReleasables and create cache files as we go
  242. for component in BuildReleasables.keys():
  243. BuildReleasables[component].Finalise()
  244. createCacheFile(BuildReleasables[component], os.path.abspath(options.outputpath), options.sourceexports, options.verbose)
  245. print "sbsv2cache: finished"
  246. if __name__ == "__main__":
  247. main()