raptor_xml.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. #
  2. # Copyright (c) 2007-2011 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. import os
  18. import raptor_data
  19. import xml.dom.minidom
  20. import re
  21. import generic_path
  22. # raptor_xml module attributes
  23. namespace = "http://symbian.com/xml/build"
  24. xsdVersion = "build/2_0.xsd"
  25. xsdIgnore = "build/666.xsd"
  26. _constructors = {"alias":raptor_data.Alias,
  27. "aliasRef":raptor_data.AliasRef,
  28. "append":raptor_data.Append,
  29. "env":raptor_data.Env,
  30. "group":raptor_data.Group,
  31. "groupRef":raptor_data.GroupRef,
  32. "interface":raptor_data.Interface,
  33. "interfaceRef":raptor_data.InterfaceRef,
  34. "param":raptor_data.Parameter,
  35. "paramgroup":raptor_data.ParameterGroup,
  36. "prepend":raptor_data.Prepend,
  37. "set":raptor_data.Set,
  38. "spec":raptor_data.Specification,
  39. "var":raptor_data.Variant,
  40. "varRef":raptor_data.VariantRef}
  41. # raptor_xml module classes
  42. class XMLError(Exception):
  43. pass
  44. # raptor_xml module functions
  45. def Read(Raptor, filename):
  46. "Read in a Raptor XML document"
  47. # try to read and parse the XML file
  48. try:
  49. dom = xml.dom.minidom.parse(filename)
  50. except: # a whole bag of exceptions can be raised here
  51. raise XMLError
  52. # <build> is always the root element
  53. build = dom.documentElement
  54. objects = []
  55. fileVersion = build.getAttribute("xsi:schemaLocation")
  56. # ignore the file it matches the "invalid" schema
  57. if fileVersion.endswith(xsdIgnore):
  58. return objects
  59. # check that the file matches the expected schema
  60. if not fileVersion.endswith(xsdVersion):
  61. Raptor.Warn("file '{0}' uses schema '{1}' which does not end with the expected version '{2}'".format(filename, fileVersion, xsdVersion))
  62. # create a Data Model object from each sub-element
  63. for child in build.childNodes:
  64. if child.namespaceURI == namespace \
  65. and child.nodeType == child.ELEMENT_NODE:
  66. try:
  67. o = XMLtoDataModel(Raptor, child)
  68. if o is not None:
  69. objects.append(o)
  70. except raptor_data.InvalidChildError:
  71. Raptor.Warn("Invalid element {0} in {1}".format(child.localName, filename))
  72. # discard the XML
  73. dom.unlink()
  74. return objects
  75. def XMLtoDataModel(Raptor, node):
  76. "Create a data-model object from an XML element"
  77. # look-up a function to create an object from the node name
  78. try:
  79. constructor = _constructors[node.localName]
  80. except KeyError:
  81. Raptor.Warn("Unknown element {0}".format(node.localName))
  82. return
  83. model = constructor()
  84. # deal with the attributes first
  85. if node.hasAttributes():
  86. for i in range(node.attributes.length):
  87. attribute = node.attributes.item(i)
  88. try:
  89. model.SetProperty(attribute.localName, attribute.value)
  90. except raptor_data.InvalidPropertyError:
  91. Raptor.Warn("Can't set attribute {0} for element {1}".format(attribute.localName, node.localName))
  92. # add the sub-elements
  93. for child in node.childNodes:
  94. if child.namespaceURI == namespace \
  95. and child.nodeType == child.ELEMENT_NODE:
  96. try:
  97. gc = XMLtoDataModel(Raptor, child)
  98. if gc is not None:
  99. model.AddChild(gc)
  100. except raptor_data.InvalidChildError:
  101. Raptor.Warn("Can't add child {0} to element {1}".format(child.localName, node.localName))
  102. # only return a valid object (or raise error)
  103. if model.Valid():
  104. if model.IsApplicable():
  105. return model
  106. else:
  107. return None
  108. else:
  109. raise raptor_data.InvalidChildError
  110. class SystemModelComponent(generic_path.Path):
  111. """Path sub-class that wraps up a component bld.inf file with
  112. system_definition.xml context information."""
  113. def __init__(self, aBldInfFile, aLayerName, aContainerNames, aSystemDefinitionFile, aSystemDefinitionBase, aSystemDefinitionVersion):
  114. generic_path.Path.__init__(self, aBldInfFile.Absolute().path)
  115. self.__ContainerNames = aContainerNames
  116. self.__LayerName = aLayerName
  117. self.__SystemDefinitionFile = aSystemDefinitionFile
  118. self.__SystemDefinitionBase = aSystemDefinitionBase
  119. self.__SystemDefinitionVersion = aSystemDefinitionVersion
  120. def GetSystemDefinitionFile(self):
  121. return self.__SystemDefinitionFile
  122. def GetSystemDefinitionBase(self):
  123. return self.__SystemDefinitionBase
  124. def GetSystemDefinitionVersion(self):
  125. return self.__SystemDefinitionVersion
  126. def GetLayerName(self):
  127. return self.__LayerName
  128. def GetContainerName(self, aContainerType):
  129. if self.__ContainerNames.has_key(aContainerType):
  130. return self.__ContainerNames[aContainerType]
  131. return ""
  132. class SystemModel(object):
  133. """A representation of the SystemModel section of a Symbian system_definition.xml file."""
  134. def __init__(self, aLogger, aSystemDefinitionFile = None, aSystemDefinitionBase = None, aDoRead = True):
  135. self.__Logger = aLogger
  136. if aSystemDefinitionFile:
  137. self.__SystemDefinitionFile = aSystemDefinitionFile.GetLocalString()
  138. else:
  139. self.__SystemDefinitionFile = generic_path.Path('undefined').GetLocalString()
  140. if aSystemDefinitionBase:
  141. self.__SystemDefinitionBase = aSystemDefinitionBase.GetLocalString()
  142. else:
  143. self.__SystemDefinitionBase = generic_path.Path('undefined').GetLocalString()
  144. self.__Version = {'MAJOR':0,'MID':0,'MINOR':0}
  145. self.__IdAttribute = "name"
  146. self.__ComponentRoot = ""
  147. self.__TotalComponents = 0
  148. self.__LayerList = []
  149. self.__LayerDetails = {}
  150. self.__MissingBldInfs = {}
  151. self.__DOM = None
  152. self.__SystemDefinitionElement = None
  153. if aDoRead:
  154. if self.__Read():
  155. if self.__Validate():
  156. self.__Parse()
  157. if self.__DOM:
  158. self.__DOM.unlink()
  159. def HasLayer(self, aLayer):
  160. return aLayer in self.__LayerList
  161. def GetLayerNames(self):
  162. return self.__LayerList
  163. def AddLayer(self, layer):
  164. """Add a layer with components to this system definition"""
  165. for c in layer.children:
  166. # c.layername takes precedence over layer.name; but if
  167. # either is blank, use the non-blank one.
  168. self.AddComponent(c, c.layername or layer.name)
  169. def AddComponent(self, aComponent, layername):
  170. '''Add a dummy component, sufficient for the purposes of
  171. writing a new system definition file. Argument is a Raptor
  172. Component object.
  173. '''
  174. if layername == '':
  175. raise Exception("Can't add a component ({0}) without a layer name"
  176. " to a system defintion file".format(str(aComponent.bldinf_filename)))
  177. containers = {'layer':layername,'component':aComponent.name}
  178. component = SystemModelComponent(aComponent.bldinf_filename, layername, containers, self.__SystemDefinitionFile, self.__SystemDefinitionBase, self.__Version)
  179. if not layername in self.__LayerList:
  180. self.__LayerList.append(layername)
  181. if not self.__LayerDetails.has_key(layername):
  182. self.__LayerDetails[layername] = []
  183. self.__LayerDetails[layername].append(component)
  184. def GetLayerComponents(self, aLayer):
  185. if not self.HasLayer(aLayer):
  186. self.__Logger.Error("System Definition layer \"{0}\" does not exist in {1}".format(aLayer, self.__SystemDefinitionFile))
  187. return []
  188. return self.__LayerDetails[aLayer]
  189. def IsLayerBuildable(self, aLayer):
  190. if aLayer in self.__MissingBldInfs:
  191. for missingbldinf in self.__MissingBldInfs[aLayer]:
  192. self.__Logger.Error("System Definition layer \"{0}\""
  193. " from system definition file \"{1}\""
  194. " refers to non existent bld.inf file {2}".format(aLayer, self.__SystemDefinitionFile, missingbldinf))
  195. if len(self.GetLayerComponents(aLayer)):
  196. return True
  197. return False
  198. def GetAllComponents(self):
  199. components = []
  200. for layer in self.GetLayerNames():
  201. components.extend(self.GetLayerComponents(layer))
  202. return components
  203. def DumpLayerInfo(self, aLayer):
  204. if self.HasLayer(aLayer):
  205. self.__Logger.Info("Found {0} bld.inf references in layer \"{1}\"".format(len(self.GetLayerComponents(aLayer)), aLayer))
  206. def DumpInfo(self):
  207. self.__Logger.Info("Found {0} bld.inf references in {1} within {2} layers:".format(len(self.GetAllComponents()), self.__SystemDefinitionFile, len(self.GetLayerNames())))
  208. self.__Logger.Info("\t{0}".format(", ".join(self.GetLayerNames())))
  209. self.__Logger.InfoDiscovery(object_type = "layers",
  210. count = len(self.GetLayerNames()))
  211. self.__Logger.InfoDiscovery(object_type = "bld.inf references",
  212. count = len(self.GetAllComponents()))
  213. def Write(self, aFilename):
  214. """Write out a system definition that can be used to create an
  215. identical SystemModel object.
  216. Note it isn't guaranteed to be a valid system definition - just one
  217. that will unserialise to an object identical to this one
  218. """
  219. impl = xml.dom.minidom.getDOMImplementation()
  220. self.__DOM = impl.createDocument(None, "SystemDefinition", None)
  221. self.__SystemDefinitionElement = self.__DOM.documentElement
  222. self.__DOM.insertBefore(self.__DOM.createComment('This document is generated by Raptor. Please do not edit.'),self.__SystemDefinitionElement)
  223. self.__SystemDefinitionElement.setAttribute('name','MCL')
  224. self.__SystemDefinitionElement.setAttribute('schema','2.0.0')
  225. systemModelNode = self.__DOM.createElement('systemModel')
  226. self.__SystemDefinitionElement.appendChild(systemModelNode)
  227. for layer in self.__LayerList:
  228. if len(self.__LayerDetails[layer]) == 0:
  229. continue
  230. if layer == '':
  231. self.__Logger.Error("Can't write out layer with no name to "+aFilename)
  232. else:
  233. layerNode = self.__DOM.createElement('layer')
  234. layerNode.setAttribute('name',layer)
  235. systemModelNode.appendChild(layerNode)
  236. for component in self.__LayerDetails[layer]:
  237. componentNode = self.__DOM.createElement('component')
  238. componentNode.setAttribute('name',component.GetContainerName('component'))
  239. layerNode.appendChild(componentNode)
  240. path = str(component)
  241. unitNode = self.__DOM.createElement('unit')
  242. unitNode.setAttribute('bldFile',path)
  243. componentNode.appendChild(unitNode)
  244. # Record that we haven't stripped the file names off our bld.infs
  245. self.__SystemDefinitionElement.setAttribute('fullbldinfs','True')
  246. self.__DOM.writexml(open(aFilename,"w"),newl="\n",indent="",addindent="\t")
  247. self.__DOM.unlink()
  248. def __Read(self):
  249. if not os.path.exists(self.__SystemDefinitionFile):
  250. self.__Logger.Error("System Definition file {0} does not exist".format(self.__SystemDefinitionFile))
  251. return False
  252. self.__Logger.Info("System Definition file {0}".format(self.__SystemDefinitionFile))
  253. # try to read the XML file
  254. try:
  255. self.__DOM = xml.dom.minidom.parse(self.__SystemDefinitionFile)
  256. except: # a whole bag of exceptions can be raised here
  257. self.__Logger.Error("Failed to parse XML file {0}".format(self.__SystemDefinitionFile))
  258. return False
  259. # <SystemDefinition> is always the root element
  260. self.__SystemDefinitionElement = self.__DOM.documentElement
  261. return True
  262. def __Validate(self):
  263. # account for different schema versions in processing
  264. # old format : version >= 1.3.0
  265. # new format : version >= 2.0.0 (assume later versions are compatible...at least for now)
  266. version = re.match(r'(?P<MAJOR>\d)\.(?P<MID>\d)(\.(?P<MINOR>\d))?', self.__SystemDefinitionElement.getAttribute("schema"))
  267. if not version:
  268. self.__Logger.Error("Cannot determine schema version of XML file {0}".format(self.__SystemDefinitionFile))
  269. return False
  270. self.__Version['MAJOR'] = int(version.group('MAJOR'))
  271. self.__Version['MID'] = int(version.group('MID'))
  272. self.__Version['MINOR'] = int(version.group('MINOR'))
  273. self.__fullbldinfs = None
  274. if self.__SystemDefinitionElement.hasAttribute('fullbldinfs'):
  275. # Lower case it since we're not evil
  276. if self.__SystemDefinitionElement.getAttribute('fullbldinfs').lower() == 'true':
  277. self.__fullbldinfs = 1
  278. if self.__Version['MAJOR'] == 1 and self.__Version['MID'] > 2:
  279. self.__ComponentRoot = self.__SystemDefinitionBase
  280. elif self.__Version['MAJOR'] == 2 or self.__Version['MAJOR'] == 3:
  281. # 2.0.x and 3.0.0 formats support SOURCEROOT or SRCROOT as an environment specified base - we respect this, unless
  282. # explicitly overridden on the command line
  283. if os.environ.has_key('SRCROOT'):
  284. self.__ComponentRoot = generic_path.Path(os.environ['SRCROOT'])
  285. elif os.environ.has_key('SOURCEROOT'):
  286. self.__ComponentRoot = generic_path.Path(os.environ['SOURCEROOT'])
  287. if self.__SystemDefinitionBase and self.__SystemDefinitionBase != ".":
  288. self.__ComponentRoot = self.__SystemDefinitionBase
  289. if os.environ.has_key('SRCROOT'):
  290. self.__Logger.Info("Command line specified System Definition file base \'{0}\'"
  291. " overriding environment SRCROOT \'{1}\'".format(self.__SystemDefinitionBase, os.environ['SRCROOT']))
  292. elif os.environ.has_key('SOURCEROOT'):
  293. self.__Logger.Info("Command line specified System Definition file base \'{0}\'"
  294. " overriding environment SOURCEROOT \'{1}\'".format(self.__SystemDefinitionBase, os.environ['SOURCEROOT']))
  295. else:
  296. self.__Logger.Error("Cannot process schema version {0} of file {1}".format(version.string, self.__SystemDefinitionFile))
  297. return False
  298. if self.__Version['MAJOR'] >= 3:
  299. # id is the unique identifier for 3.0 and later schema
  300. self.__IdAttribute = "id"
  301. return True
  302. def __Parse(self):
  303. # For 2.0 and earlier: find the <systemModel> element (there can be 0 or 1) and search any <layer> elements for <unit> elements with "bldFile" attributes
  304. # the <layer> context of captured "bldFile" attributes is recorded as we go
  305. # For 3.0 and later, process any architectural topmost element, use the topmost element with an id as the "layer"
  306. for child in self.__SystemDefinitionElement.childNodes:
  307. if child.localName in ["systemModel", "layer", "package", "collection", "component"]:
  308. self.__ProcessSystemModelElement(child)
  309. def __CreateComponent(self, aBldInfFile, aUnitElement):
  310. # take a resolved bld.inf file and associated <unit/> element and returns a populated Component object
  311. containers = {}
  312. self.__GetElementContainers(aUnitElement, containers)
  313. layer = self.__GetEffectiveLayer(aUnitElement)
  314. component = SystemModelComponent(aBldInfFile, layer, containers, self.__SystemDefinitionFile, self.__SystemDefinitionBase, self.__Version)
  315. return component
  316. def __GetEffectiveLayer(self, aElement):
  317. # return layer ID in effect for this unit. This is either the ID of a parent
  318. # element called 'layer' or the ID of the first parent whose own parent does
  319. # not have an ID. For a well formed v1.x or 2.x system definition these two
  320. # criteria must point to the same element's ID. A well formed v3.x system
  321. # definition does not need a 'layer' component necessarily.
  322. # Never call this on the root element
  323. if aElement.tagName == "layer" and aElement.hasAttribute(self.__IdAttribute):
  324. # Note if we encounter an element named 'layer' before we reach the end
  325. # of the id stack, we'll use that.
  326. return aElement.getAttribute(self.__IdAttribute)
  327. elif aElement.parentNode.hasAttribute(self.__IdAttribute):
  328. return self.__GetEffectiveLayer(aElement.parentNode)
  329. elif aElement.hasAttribute(self.__IdAttribute):
  330. # Note that if we encounter the end of the id stack before we encounter
  331. # an element called 'layer', we'll use that regardless of whether there's
  332. # a 'layer' element further up.
  333. return aElement.getAttribute(self.__IdAttribute)
  334. return ""
  335. def __GetElementContainers(self, aElement, aContainers):
  336. # take a <unit/> element and creates a type->name dictionary of all of its parent containers
  337. # We're only interested in parent nodes if they're not the top-most node
  338. if aElement.parentNode.parentNode:
  339. parent = aElement.parentNode
  340. name = parent.getAttribute(self.__IdAttribute)
  341. if name:
  342. aContainers[parent.tagName] = name
  343. self.__GetElementContainers(parent, aContainers)
  344. def __ProcessSystemModelMetaElement(self, aElement):
  345. # stub method - may deal with metadata elements at some point in the future
  346. return
  347. def __ProcessSystemModelElement(self, aElement):
  348. """Search for XML <unit/> elements with 'bldFile' attributes and resolve concrete bld.inf locations
  349. with an appreciation of different schema versions."""
  350. # Metadata elements are processed separately - there are no further child nodes
  351. # to process in this context
  352. if aElement.tagName == "meta" :
  353. return self.__ProcessSystemModelMetaElement(aElement)
  354. # The effective "layer" is the item whose parent does not have an id (or name in 2.x and earlier)
  355. if not aElement.parentNode.hasAttribute(self.__IdAttribute) :
  356. currentLayer = aElement.getAttribute(self.__IdAttribute)
  357. if not self.__LayerDetails.has_key(currentLayer):
  358. self.__LayerDetails[currentLayer] = []
  359. if not currentLayer in self.__LayerList:
  360. self.__LayerList.append(currentLayer)
  361. elif aElement.tagName == "unit" and aElement.hasAttributes():
  362. bldFileValue = aElement.getAttribute("bldFile")
  363. if bldFileValue:
  364. bldInfRoot = self.__ComponentRoot
  365. if self.__Version['MAJOR'] == 1:
  366. # version 1.x schema paths can use DOS slashes
  367. bldFileValue = bldFileValue.replace('\\', '/')
  368. elif self.__Version['MAJOR'] >= 2:
  369. # version 2.x.x schema paths are subject to a "root" attribute off-set, if it exists
  370. rootValue = aElement.getAttribute("root")
  371. if rootValue:
  372. if os.environ.has_key(rootValue):
  373. bldInfRoot = generic_path.Path(os.environ[rootValue])
  374. else:
  375. # Assume that this is an error i.e. don't attempt to resolve in relation to SOURCEROOT
  376. bldInfRoot = None
  377. self.__Logger.Error("Cannot resolve \'root\' attribute value \"{0}\" in {1}".format(rootValue, self.__SystemDefinitionFile))
  378. return
  379. bldinfval = generic_path.Path(bldFileValue)
  380. if self.__Version['MAJOR'] < 3:
  381. # absolute paths are not changed by root var in 1.x and 2.x
  382. if not bldinfval.isAbsolute() and bldInfRoot:
  383. bldinfval = generic_path.Join(bldInfRoot, bldinfval)
  384. else:
  385. # relative paths for v3
  386. if not bldinfval.isAbsolute():
  387. bldinfval = generic_path.Join(generic_path.Join(self.__SystemDefinitionFile).Dir(),bldinfval)
  388. # absolute paths for v3
  389. # are relative to bldInfRoot if set, or relative to the drive root otherwise
  390. elif bldInfRoot:
  391. bldinfval = generic_path.Join(bldInfRoot, bldinfval)
  392. if self.__fullbldinfs:
  393. bldinf = bldinfval.FindCaseless()
  394. else:
  395. bldinf = generic_path.Join(bldinfval, "bld.inf").FindCaseless()
  396. if bldinf == None:
  397. # recording layers containing non existent bld.infs
  398. bldinfname = bldinfval.GetLocalString()
  399. if not self.__fullbldinfs:
  400. bldinfname = bldinfname+'/'+'bld.inf'
  401. layer = self.__GetEffectiveLayer(aElement)
  402. if not layer in self.__MissingBldInfs:
  403. self.__MissingBldInfs[layer]=[]
  404. self.__MissingBldInfs[layer].append(bldinfname)
  405. else:
  406. component = self.__CreateComponent(bldinf, aElement)
  407. layer = component.GetLayerName()
  408. if layer:
  409. self.__LayerDetails[layer].append(component)
  410. self.__TotalComponents += 1
  411. else:
  412. self.__Logger.Error("No containing layer found for {0} in {1}".format(str(bldinf), self.__SystemDefinitionFile))
  413. # search the sub-elements
  414. for child in aElement.childNodes:
  415. if child.nodeType == child.ELEMENT_NODE:
  416. self.__ProcessSystemModelElement(child)
  417. # end of the raptor_xml module