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