sbsv2/raptor/python/raptor_xml.py
changeset 2 39c28ec933dd
child 13 c327db0664bb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/python/raptor_xml.py	Mon May 10 19:54:49 2010 +0100
@@ -0,0 +1,363 @@
+#
+# Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+# raptor_xml module
+#
+
+import os
+import raptor_data
+import raptor_utilities
+import xml.dom.minidom
+import re
+import generic_path
+
+# raptor_xml module attributes
+
+namespace = "http://symbian.com/xml/build"
+xsdVersion = "build/2_0.xsd"
+xsdIgnore = "build/666.xsd"
+
+_constructors = {"alias":raptor_data.Alias,
+				 "aliasRef":raptor_data.AliasRef,
+				 "append":raptor_data.Append,
+				 "env":raptor_data.Env,
+				 "group":raptor_data.Group,
+				 "groupRef":raptor_data.GroupRef,
+				 "interface":raptor_data.Interface,
+				 "interfaceRef":raptor_data.InterfaceRef,
+				 "param":raptor_data.Parameter,
+				 "paramgroup":raptor_data.ParameterGroup,
+				 "prepend":raptor_data.Prepend,
+				 "set":raptor_data.Set,
+				 "spec":raptor_data.Specification,
+				 "var":raptor_data.Variant,
+				 "varRef":raptor_data.VariantRef}
+
+
+# raptor_xml module classes
+
+class XMLError(Exception):
+	pass
+
+# raptor_xml module functions
+
+def Read(Raptor, filename):
+	"Read in a Raptor XML document"
+
+	# try to read and parse the XML file
+	try:
+		dom = xml.dom.minidom.parse(filename)
+
+	except: # a whole bag of exceptions can be raised here
+		raise XMLError
+
+	# <build> is always the root element
+	build = dom.documentElement
+	objects = []
+
+	fileVersion = build.getAttribute("xsi:schemaLocation")
+	
+	# ignore the file it matches the "invalid" schema
+	if fileVersion.endswith(xsdIgnore):
+		return objects
+		
+	# check that the file matches the expected schema
+	if not fileVersion.endswith(xsdVersion):
+		Raptor.Warn("file '%s' uses schema '%s' which does not end with the expected version '%s'", filename, fileVersion, xsdVersion)
+		
+	# create a Data Model object from each sub-element
+	for child in build.childNodes:
+		if child.namespaceURI == namespace \
+		and child.nodeType == child.ELEMENT_NODE:
+			try:
+				o = XMLtoDataModel(Raptor, child)
+				if o is not None:
+					objects.append(o)
+			except raptor_data.InvalidChildError:
+				Raptor.Warn("Invalid element %s in %s", child.localName, filename)
+
+	# discard the XML
+	dom.unlink()
+	return objects
+
+
+def XMLtoDataModel(Raptor, node):
+	"Create a data-model object from an XML element"
+
+	# look-up a function to create an object from the node name
+	try:
+		constructor = _constructors[node.localName]
+
+	except KeyError:
+		Raptor.Warn("Unknown element %s", node.localName)
+		return
+
+	model = constructor()
+
+	# deal with the attributes first
+	if node.hasAttributes():
+		for i in range(node.attributes.length):
+			attribute = node.attributes.item(i)
+			try:
+
+				model.SetProperty(attribute.localName, attribute.value)
+
+			except raptor_data.InvalidPropertyError:
+				Raptor.Warn("Can't set attribute %s for element %s",
+							 attribute.localName, node.localName)
+
+	# add the sub-elements
+	for child in node.childNodes:
+		if child.namespaceURI == namespace \
+		and child.nodeType == child.ELEMENT_NODE:
+			try:
+				gc = XMLtoDataModel(Raptor, child)
+				if gc is not None:
+					model.AddChild(gc)
+
+			except raptor_data.InvalidChildError:
+				Raptor.Warn("Can't add child %s to element %s",
+							 child.localName, node.localName)
+
+	# only return a valid object (or raise error)
+	if model.Valid():
+		if model.IsApplicable():
+			return model
+		else:
+			return None
+	else:
+		raise raptor_data.InvalidChildError
+
+
+class SystemModelComponent(generic_path.Path):
+	"""Path sub-class that wraps up a component bld.inf file with
+	system_definition.xml context information."""
+
+	def __init__(self, aBldInfFile, aContainerNames, aSystemDefinitionFile, aSystemDefinitionBase, aSystemDefinitionVersion):
+		generic_path.Path.__init__(self, aBldInfFile.Absolute().path)
+		self.__ContainerNames = aContainerNames
+		self.__SystemDefinitionFile = aSystemDefinitionFile
+		self.__SystemDefinitionBase = aSystemDefinitionBase
+		self.__SystemDefinitionVersion = aSystemDefinitionVersion
+
+	def GetSystemDefinitionFile(self):
+		return self.__SystemDefinitionFile
+
+	def GetSystemDefinitionBase(self):
+		return self.__SystemDefinitionBase
+
+	def GetSystemDefinitionFile(self):
+		return self.__SystemDefinitionVersion
+
+	def GetContainerName(self, aContainerType):
+		if self.__ContainerNames.has_key(aContainerType):
+		  return self.__ContainerNames[aContainerType]
+		return ""
+
+
+class SystemModel(object):
+	"""A representation of the SystemModel section of a Symbian system_definition.xml file."""
+
+	def __init__(self, aLogger, aSystemDefinitionFile, aSystemDefinitionBase):
+		self.__Logger = aLogger
+		self.__SystemDefinitionFile = aSystemDefinitionFile.GetLocalString()
+		self.__SystemDefinitionBase = aSystemDefinitionBase.GetLocalString()
+		self.__Version = {'MAJOR':0,'MID':0,'MINOR':0}
+		self.__ComponentRoot = ""
+		self.__TotalComponents = 0
+		self.__LayerList = []
+		self.__LayerDetails = {}
+
+		self.__DOM = None
+		self.__SystemDefinitionElement = None
+
+		if self.__Read():
+			if self.__Validate():
+				self.__Parse()
+
+		if self.__DOM:
+			self.__DOM.unlink()
+
+	def HasLayer(self, aLayer):
+		return aLayer in self.__LayerList
+
+	def GetLayerNames(self):
+		return self.__LayerList
+
+	def GetLayerComponents(self, aLayer):
+		if not self.HasLayer(aLayer):
+			self.__Logger.Error("System Definition layer \"%s\" does not exist in %s", aLayer, self.__SystemDefinitionFile)
+			return []
+
+		return self.__LayerDetails[aLayer]
+
+	def IsLayerBuildable(self, aLayer):
+		if len(self.GetLayerComponents(aLayer)):
+			return True
+		return False
+
+	def GetAllComponents(self):
+		components = []
+
+		for layer in self.GetLayerNames():
+			components.extend(self.GetLayerComponents(layer))
+
+		return components
+
+	def DumpLayerInfo(self, aLayer):
+		if self.HasLayer(aLayer):
+			self.__Logger.Info("Found %d bld.inf references in layer \"%s\"", len(self.GetLayerComponents(aLayer)), aLayer)
+
+	def DumpInfo(self):
+		self.__Logger.Info("Found %d bld.inf references in %s within %d layers:", len(self.GetAllComponents()), self.__SystemDefinitionFile, len(self.GetLayerNames()))
+		self.__Logger.Info("\t%s", ", ".join(self.GetLayerNames()))
+
+	def __Read(self):
+		if not os.path.exists(self.__SystemDefinitionFile):
+			self.__Logger.Error("System Definition file %s does not exist", self.__SystemDefinitionFile)
+			return False
+
+		self.__Logger.Info("System Definition file %s", self.__SystemDefinitionFile)
+
+		# try to read the XML file
+		try:
+			self.__DOM = xml.dom.minidom.parse(self.__SystemDefinitionFile)
+
+		except: # a whole bag of exceptions can be raised here
+			self.__Logger.Error("Failed to parse XML file %s", self.__SystemDefinitionFile)
+			return False
+
+		# <SystemDefinition> is always the root element
+		self.__SystemDefinitionElement = self.__DOM.documentElement
+
+		return True
+
+	def __Validate(self):
+		# account for different schema versions in processing
+		# old format : version >= 1.3.0
+		# new format : version >= 2.0.0 (assume later versions are compatible...at least for now)
+		version = re.match(r'(?P<MAJOR>\d)\.(?P<MID>\d)(\.(?P<MINOR>\d))?', self.__SystemDefinitionElement.getAttribute("schema"))
+
+		if not version:
+			self.__Logger.Error("Cannot determine schema version of XML file %s", self.__SystemDefinitionFile)
+			return False
+
+		self.__Version['MAJOR'] = int(version.group('MAJOR'))
+		self.__Version['MID'] = int(version.group('MID'))
+		self.__Version['MINOR'] = int(version.group('MINOR'))
+
+		if self.__Version['MAJOR'] == 1 and self.__Version['MID'] > 2:
+			self.__ComponentRoot = self.__SystemDefinitionBase
+		elif self.__Version['MAJOR'] == 2:
+			# 2.0.0 format supports SOURCEROOT as an environment specified base - we respect this, unless
+			# explicitly overridden on the command line
+			if os.environ.has_key('SOURCEROOT'):
+				self.__ComponentRoot = generic_path.Path(os.environ['SOURCEROOT'])
+			if self.__SystemDefinitionBase and self.__SystemDefinitionBase != ".":
+				self.__ComponentRoot = self.__SystemDefinitionBase
+				if os.environ.has_key('SOURCEROOT'):
+					self.__Logger.Info("Command line specified System Definition file base \'%s\' overriding environment SOURCEROOT \'%s\'", self.__SystemDefinitionBase, os.environ['SOURCEROOT'])
+		else:
+			self.__Logger.Error("Cannot process schema version %s of file %s", version.string, self.__SystemDefinitionFile)
+			return False
+
+		return True
+
+	def __Parse(self):
+		# find the <systemModel> element (there can be 0 or 1) and search any <layer> elements for <unit> elements with "bldFile" attributes
+		# the <layer> context of captured "bldFile" attributes is recorded as we go
+		for child in self.__SystemDefinitionElement.childNodes:
+			if child.localName == "systemModel":
+				self.__ProcessSystemModelElement(child)
+
+	def __CreateComponent(self, aBldInfFile, aUnitElement):
+		# take a resolved bld.inf file and associated <unit/> element and returns a populated Component object
+		containers = {}
+		self.__GetElementContainers(aUnitElement, containers)
+		component = SystemModelComponent(aBldInfFile, containers, self.__SystemDefinitionFile, self.__SystemDefinitionBase, self.__Version)
+
+		return component
+
+	def __GetElementContainers(self, aElement, aContainers):
+		# take a <unit/> element and creates a type->name dictionary of all of its parent containers
+		# We're only interested in parent nodes if they're not the top-most node
+		if aElement.parentNode.parentNode:
+			parent = aElement.parentNode
+			name = parent.getAttribute("name")
+
+			if name:
+				aContainers[parent.tagName] = name
+
+			self.__GetElementContainers(parent, aContainers)
+
+	def __ProcessSystemModelElement(self, aElement):
+		"""Search for XML <unit/> elements with 'bldFile' attributes and resolve concrete bld.inf locations
+		with an appreciation of different schema versions."""
+
+		if aElement.tagName == "layer":
+			currentLayer = aElement.getAttribute("name")
+
+			if not self.__LayerDetails.has_key(currentLayer):
+				self.__LayerDetails[currentLayer] = []
+
+			if not currentLayer in self.__LayerList:
+				self.__LayerList.append(currentLayer)
+
+		elif aElement.tagName == "unit" and aElement.hasAttributes():
+			bldFileValue = aElement.getAttribute("bldFile")
+
+			if bldFileValue:
+				bldInfRoot = self.__ComponentRoot
+
+				if self.__Version['MAJOR'] == 1 and self.__Version['MID'] == 4:
+					# version 1.4.x schema paths can use DOS slashes
+					bldFileValue = raptor_utilities.convertToUnixSlash(bldFileValue)
+				elif self.__Version['MAJOR'] == 2:
+					# version 2.x.x schema paths are subject to a "root" attribute off-set, if it exists
+					rootValue = aElement.getAttribute("root")
+
+					if rootValue:
+						if os.environ.has_key(rootValue):
+							bldInfRoot = generic_path.Path(os.environ[rootValue])
+						else:
+							# Assume that this is an error i.e. don't attempt to resolve in relation to SOURCEROOT
+							bldInfRoot = None
+							self.__Logger.Error("Cannot resolve \'root\' attribute value \"%s\" in %s", rootValue, self.__SystemDefinitionFile)
+							return
+
+				group = generic_path.Path(bldFileValue)
+
+				if not group.isAbsolute() and bldInfRoot:
+					group = generic_path.Join(bldInfRoot, group)
+
+				bldinf = generic_path.Join(group, "bld.inf").FindCaseless()
+
+				if bldinf == None:
+					self.__Logger.Error("No bld.inf found at %s in %s", group.GetLocalString(), self.__SystemDefinitionFile)
+				else:
+					component = self.__CreateComponent(bldinf, aElement)
+					layer = component.GetContainerName("layer")
+					if layer:
+						self.__LayerDetails[layer].append(component)
+						self.__TotalComponents += 1
+					else:
+						self.__Logger.Error("No containing layer found for %s in %s", str(bldinf), self.__SystemDefinitionFile)
+
+		# search the sub-elements
+		for child in aElement.childNodes:
+			if child.nodeType == child.ELEMENT_NODE:
+				self.__ProcessSystemModelElement(child)
+
+
+# end of the raptor_xml module