|
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 |