|
1 # |
|
2 # Copyright (c) 2007-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 # raptor_xml module |
|
16 # |
|
17 |
|
18 import os |
|
19 import raptor_data |
|
20 import raptor_utilities |
|
21 import xml.dom.minidom |
|
22 import re |
|
23 import generic_path |
|
24 |
|
25 # raptor_xml module attributes |
|
26 |
|
27 namespace = "http://symbian.com/xml/build" |
|
28 xsdVersion = "build/2_0.xsd" |
|
29 xsdIgnore = "build/666.xsd" |
|
30 |
|
31 _constructors = {"alias":raptor_data.Alias, |
|
32 "aliasRef":raptor_data.AliasRef, |
|
33 "append":raptor_data.Append, |
|
34 "env":raptor_data.Env, |
|
35 "group":raptor_data.Group, |
|
36 "groupRef":raptor_data.GroupRef, |
|
37 "interface":raptor_data.Interface, |
|
38 "interfaceRef":raptor_data.InterfaceRef, |
|
39 "param":raptor_data.Parameter, |
|
40 "paramgroup":raptor_data.ParameterGroup, |
|
41 "prepend":raptor_data.Prepend, |
|
42 "set":raptor_data.Set, |
|
43 "spec":raptor_data.Specification, |
|
44 "var":raptor_data.Variant, |
|
45 "varRef":raptor_data.VariantRef} |
|
46 |
|
47 |
|
48 # raptor_xml module classes |
|
49 |
|
50 class XMLError(Exception): |
|
51 pass |
|
52 |
|
53 # raptor_xml module functions |
|
54 |
|
55 def Read(Raptor, filename): |
|
56 "Read in a Raptor XML document" |
|
57 |
|
58 # try to read and parse the XML file |
|
59 try: |
|
60 dom = xml.dom.minidom.parse(filename) |
|
61 |
|
62 except: # a whole bag of exceptions can be raised here |
|
63 raise XMLError |
|
64 |
|
65 # <build> is always the root element |
|
66 build = dom.documentElement |
|
67 objects = [] |
|
68 |
|
69 fileVersion = build.getAttribute("xsi:schemaLocation") |
|
70 |
|
71 # ignore the file it matches the "invalid" schema |
|
72 if fileVersion.endswith(xsdIgnore): |
|
73 return objects |
|
74 |
|
75 # check that the file matches the expected schema |
|
76 if not fileVersion.endswith(xsdVersion): |
|
77 Raptor.Warn("file '%s' uses schema '%s' which does not end with the expected version '%s'", filename, fileVersion, xsdVersion) |
|
78 |
|
79 # create a Data Model object from each sub-element |
|
80 for child in build.childNodes: |
|
81 if child.namespaceURI == namespace \ |
|
82 and child.nodeType == child.ELEMENT_NODE: |
|
83 try: |
|
84 o = XMLtoDataModel(Raptor, child) |
|
85 if o is not None: |
|
86 objects.append(o) |
|
87 except raptor_data.InvalidChildError: |
|
88 Raptor.Warn("Invalid element %s in %s", child.localName, filename) |
|
89 |
|
90 # discard the XML |
|
91 dom.unlink() |
|
92 return objects |
|
93 |
|
94 |
|
95 def XMLtoDataModel(Raptor, node): |
|
96 "Create a data-model object from an XML element" |
|
97 |
|
98 # look-up a function to create an object from the node name |
|
99 try: |
|
100 constructor = _constructors[node.localName] |
|
101 |
|
102 except KeyError: |
|
103 Raptor.Warn("Unknown element %s", node.localName) |
|
104 return |
|
105 |
|
106 model = constructor() |
|
107 |
|
108 # deal with the attributes first |
|
109 if node.hasAttributes(): |
|
110 for i in range(node.attributes.length): |
|
111 attribute = node.attributes.item(i) |
|
112 try: |
|
113 |
|
114 model.SetProperty(attribute.localName, attribute.value) |
|
115 |
|
116 except raptor_data.InvalidPropertyError: |
|
117 Raptor.Warn("Can't set attribute %s for element %s", |
|
118 attribute.localName, node.localName) |
|
119 |
|
120 # add the sub-elements |
|
121 for child in node.childNodes: |
|
122 if child.namespaceURI == namespace \ |
|
123 and child.nodeType == child.ELEMENT_NODE: |
|
124 try: |
|
125 gc = XMLtoDataModel(Raptor, child) |
|
126 if gc is not None: |
|
127 model.AddChild(gc) |
|
128 |
|
129 except raptor_data.InvalidChildError: |
|
130 Raptor.Warn("Can't add child %s to element %s", |
|
131 child.localName, node.localName) |
|
132 |
|
133 # only return a valid object (or raise error) |
|
134 if model.Valid(): |
|
135 if model.IsApplicable(): |
|
136 return model |
|
137 else: |
|
138 return None |
|
139 else: |
|
140 raise raptor_data.InvalidChildError |
|
141 |
|
142 |
|
143 class SystemModelComponent(generic_path.Path): |
|
144 """Path sub-class that wraps up a component bld.inf file with |
|
145 system_definition.xml context information.""" |
|
146 |
|
147 def __init__(self, aBldInfFile, aContainerNames, aSystemDefinitionFile, aSystemDefinitionBase, aSystemDefinitionVersion): |
|
148 generic_path.Path.__init__(self, aBldInfFile.Absolute().path) |
|
149 self.__ContainerNames = aContainerNames |
|
150 self.__SystemDefinitionFile = aSystemDefinitionFile |
|
151 self.__SystemDefinitionBase = aSystemDefinitionBase |
|
152 self.__SystemDefinitionVersion = aSystemDefinitionVersion |
|
153 |
|
154 def GetSystemDefinitionFile(self): |
|
155 return self.__SystemDefinitionFile |
|
156 |
|
157 def GetSystemDefinitionBase(self): |
|
158 return self.__SystemDefinitionBase |
|
159 |
|
160 def GetSystemDefinitionFile(self): |
|
161 return self.__SystemDefinitionVersion |
|
162 |
|
163 def GetContainerName(self, aContainerType): |
|
164 if self.__ContainerNames.has_key(aContainerType): |
|
165 return self.__ContainerNames[aContainerType] |
|
166 return "" |
|
167 |
|
168 |
|
169 class SystemModel(object): |
|
170 """A representation of the SystemModel section of a Symbian system_definition.xml file.""" |
|
171 |
|
172 def __init__(self, aLogger, aSystemDefinitionFile, aSystemDefinitionBase): |
|
173 self.__Logger = aLogger |
|
174 self.__SystemDefinitionFile = aSystemDefinitionFile.GetLocalString() |
|
175 self.__SystemDefinitionBase = aSystemDefinitionBase.GetLocalString() |
|
176 self.__Version = {'MAJOR':0,'MID':0,'MINOR':0} |
|
177 self.__ComponentRoot = "" |
|
178 self.__TotalComponents = 0 |
|
179 self.__LayerList = [] |
|
180 self.__LayerDetails = {} |
|
181 |
|
182 self.__DOM = None |
|
183 self.__SystemDefinitionElement = None |
|
184 |
|
185 if self.__Read(): |
|
186 if self.__Validate(): |
|
187 self.__Parse() |
|
188 |
|
189 if self.__DOM: |
|
190 self.__DOM.unlink() |
|
191 |
|
192 def HasLayer(self, aLayer): |
|
193 return aLayer in self.__LayerList |
|
194 |
|
195 def GetLayerNames(self): |
|
196 return self.__LayerList |
|
197 |
|
198 def GetLayerComponents(self, aLayer): |
|
199 if not self.HasLayer(aLayer): |
|
200 self.__Logger.Error("System Definition layer \"%s\" does not exist in %s", aLayer, self.__SystemDefinitionFile) |
|
201 return [] |
|
202 |
|
203 return self.__LayerDetails[aLayer] |
|
204 |
|
205 def IsLayerBuildable(self, aLayer): |
|
206 if len(self.GetLayerComponents(aLayer)): |
|
207 return True |
|
208 return False |
|
209 |
|
210 def GetAllComponents(self): |
|
211 components = [] |
|
212 |
|
213 for layer in self.GetLayerNames(): |
|
214 components.extend(self.GetLayerComponents(layer)) |
|
215 |
|
216 return components |
|
217 |
|
218 def DumpLayerInfo(self, aLayer): |
|
219 if self.HasLayer(aLayer): |
|
220 self.__Logger.Info("Found %d bld.inf references in layer \"%s\"", len(self.GetLayerComponents(aLayer)), aLayer) |
|
221 |
|
222 def DumpInfo(self): |
|
223 self.__Logger.Info("Found %d bld.inf references in %s within %d layers:", len(self.GetAllComponents()), self.__SystemDefinitionFile, len(self.GetLayerNames())) |
|
224 self.__Logger.Info("\t%s", ", ".join(self.GetLayerNames())) |
|
225 |
|
226 def __Read(self): |
|
227 if not os.path.exists(self.__SystemDefinitionFile): |
|
228 self.__Logger.Error("System Definition file %s does not exist", self.__SystemDefinitionFile) |
|
229 return False |
|
230 |
|
231 self.__Logger.Info("System Definition file %s", self.__SystemDefinitionFile) |
|
232 |
|
233 # try to read the XML file |
|
234 try: |
|
235 self.__DOM = xml.dom.minidom.parse(self.__SystemDefinitionFile) |
|
236 |
|
237 except: # a whole bag of exceptions can be raised here |
|
238 self.__Logger.Error("Failed to parse XML file %s", self.__SystemDefinitionFile) |
|
239 return False |
|
240 |
|
241 # <SystemDefinition> is always the root element |
|
242 self.__SystemDefinitionElement = self.__DOM.documentElement |
|
243 |
|
244 return True |
|
245 |
|
246 def __Validate(self): |
|
247 # account for different schema versions in processing |
|
248 # old format : version >= 1.3.0 |
|
249 # new format : version >= 2.0.0 (assume later versions are compatible...at least for now) |
|
250 version = re.match(r'(?P<MAJOR>\d)\.(?P<MID>\d)(\.(?P<MINOR>\d))?', self.__SystemDefinitionElement.getAttribute("schema")) |
|
251 |
|
252 if not version: |
|
253 self.__Logger.Error("Cannot determine schema version of XML file %s", self.__SystemDefinitionFile) |
|
254 return False |
|
255 |
|
256 self.__Version['MAJOR'] = int(version.group('MAJOR')) |
|
257 self.__Version['MID'] = int(version.group('MID')) |
|
258 self.__Version['MINOR'] = int(version.group('MINOR')) |
|
259 |
|
260 if self.__Version['MAJOR'] == 1 and self.__Version['MID'] > 2: |
|
261 self.__ComponentRoot = self.__SystemDefinitionBase |
|
262 elif self.__Version['MAJOR'] == 2: |
|
263 # 2.0.0 format supports SOURCEROOT as an environment specified base - we respect this, unless |
|
264 # explicitly overridden on the command line |
|
265 if os.environ.has_key('SOURCEROOT'): |
|
266 self.__ComponentRoot = generic_path.Path(os.environ['SOURCEROOT']) |
|
267 if self.__SystemDefinitionBase and self.__SystemDefinitionBase != ".": |
|
268 self.__ComponentRoot = self.__SystemDefinitionBase |
|
269 if os.environ.has_key('SOURCEROOT'): |
|
270 self.__Logger.Info("Command line specified System Definition file base \'%s\' overriding environment SOURCEROOT \'%s\'", self.__SystemDefinitionBase, os.environ['SOURCEROOT']) |
|
271 else: |
|
272 self.__Logger.Error("Cannot process schema version %s of file %s", version.string, self.__SystemDefinitionFile) |
|
273 return False |
|
274 |
|
275 return True |
|
276 |
|
277 def __Parse(self): |
|
278 # find the <systemModel> element (there can be 0 or 1) and search any <layer> elements for <unit> elements with "bldFile" attributes |
|
279 # the <layer> context of captured "bldFile" attributes is recorded as we go |
|
280 for child in self.__SystemDefinitionElement.childNodes: |
|
281 if child.localName == "systemModel": |
|
282 self.__ProcessSystemModelElement(child) |
|
283 |
|
284 def __CreateComponent(self, aBldInfFile, aUnitElement): |
|
285 # take a resolved bld.inf file and associated <unit/> element and returns a populated Component object |
|
286 containers = {} |
|
287 self.__GetElementContainers(aUnitElement, containers) |
|
288 component = SystemModelComponent(aBldInfFile, containers, self.__SystemDefinitionFile, self.__SystemDefinitionBase, self.__Version) |
|
289 |
|
290 return component |
|
291 |
|
292 def __GetElementContainers(self, aElement, aContainers): |
|
293 # take a <unit/> element and creates a type->name dictionary of all of its parent containers |
|
294 # We're only interested in parent nodes if they're not the top-most node |
|
295 if aElement.parentNode.parentNode: |
|
296 parent = aElement.parentNode |
|
297 name = parent.getAttribute("name") |
|
298 |
|
299 if name: |
|
300 aContainers[parent.tagName] = name |
|
301 |
|
302 self.__GetElementContainers(parent, aContainers) |
|
303 |
|
304 def __ProcessSystemModelElement(self, aElement): |
|
305 """Search for XML <unit/> elements with 'bldFile' attributes and resolve concrete bld.inf locations |
|
306 with an appreciation of different schema versions.""" |
|
307 |
|
308 if aElement.tagName == "layer": |
|
309 currentLayer = aElement.getAttribute("name") |
|
310 |
|
311 if not self.__LayerDetails.has_key(currentLayer): |
|
312 self.__LayerDetails[currentLayer] = [] |
|
313 |
|
314 if not currentLayer in self.__LayerList: |
|
315 self.__LayerList.append(currentLayer) |
|
316 |
|
317 elif aElement.tagName == "unit" and aElement.hasAttributes(): |
|
318 bldFileValue = aElement.getAttribute("bldFile") |
|
319 |
|
320 if bldFileValue: |
|
321 bldInfRoot = self.__ComponentRoot |
|
322 |
|
323 if self.__Version['MAJOR'] == 1 and self.__Version['MID'] == 4: |
|
324 # version 1.4.x schema paths can use DOS slashes |
|
325 bldFileValue = raptor_utilities.convertToUnixSlash(bldFileValue) |
|
326 elif self.__Version['MAJOR'] == 2: |
|
327 # version 2.x.x schema paths are subject to a "root" attribute off-set, if it exists |
|
328 rootValue = aElement.getAttribute("root") |
|
329 |
|
330 if rootValue: |
|
331 if os.environ.has_key(rootValue): |
|
332 bldInfRoot = generic_path.Path(os.environ[rootValue]) |
|
333 else: |
|
334 # Assume that this is an error i.e. don't attempt to resolve in relation to SOURCEROOT |
|
335 bldInfRoot = None |
|
336 self.__Logger.Error("Cannot resolve \'root\' attribute value \"%s\" in %s", rootValue, self.__SystemDefinitionFile) |
|
337 return |
|
338 |
|
339 group = generic_path.Path(bldFileValue) |
|
340 |
|
341 if not group.isAbsolute() and bldInfRoot: |
|
342 group = generic_path.Join(bldInfRoot, group) |
|
343 |
|
344 bldinf = generic_path.Join(group, "bld.inf").FindCaseless() |
|
345 |
|
346 if bldinf == None: |
|
347 self.__Logger.Error("No bld.inf found at %s in %s", group.GetLocalString(), self.__SystemDefinitionFile) |
|
348 else: |
|
349 component = self.__CreateComponent(bldinf, aElement) |
|
350 layer = component.GetContainerName("layer") |
|
351 if layer: |
|
352 self.__LayerDetails[layer].append(component) |
|
353 self.__TotalComponents += 1 |
|
354 else: |
|
355 self.__Logger.Error("No containing layer found for %s in %s", str(bldinf), self.__SystemDefinitionFile) |
|
356 |
|
357 # search the sub-elements |
|
358 for child in aElement.childNodes: |
|
359 if child.nodeType == child.ELEMENT_NODE: |
|
360 self.__ProcessSystemModelElement(child) |
|
361 |
|
362 |
|
363 # end of the raptor_xml module |