sbsv2/raptor/python/raptor_api.py
changeset 625 a1925fb7753a
parent 591 22486c9c7b15
--- a/sbsv2/raptor/python/raptor_api.py	Wed Jul 28 13:20:46 2010 +0100
+++ b/sbsv2/raptor/python/raptor_api.py	Thu Aug 12 09:00:16 2010 +0100
@@ -27,8 +27,22 @@
 class Reply(object):
 	"""object to return values from API calls.
 	"""
-	def __init__(self, text=""):
+	def __init__(self, text="", raptor=None):
+		self._raptor = raptor
 		self.text = text
+		
+	def _getEvaluator(self, meaning):
+		""" Note: Will pass on Evaluator constructor exceptions """
+		try:
+			return self.__evaluator
+		except AttributeError:	
+			# create an evaluator for the named configuration
+			tmp = raptor_data.Alias("tmp")
+			tmp.SetProperty("meaning", meaning)
+		
+			units = tmp.GenerateBuildUnits(self._raptor.cache)
+			self.__evaluator = self._raptor.GetEvaluator(None, units[0])
+			return self.__evaluator
 	
 	def __str__(self):
 		name = type(self).__name__.lower()
@@ -38,11 +52,18 @@
 		longend = False
 		
 		for attribute,value in self.__dict__.items():
-			if attribute != "text":
+			if attribute != "text" and not attribute.startswith('_'):
 				if isinstance(value, Reply):
 					children.append(value)
+				elif isinstance(value, list):
+					for item in value:
+						if isinstance(item, Reply):
+							children.append(item)
+						else:
+							raise BadReply(str(item)+" is not a Reply object")
 				else:
-					string += " %s='%s'" % (attribute, value)
+					if value != None: # skip attributes whose value is None
+						string += " %s='%s'" % (attribute, value)
 		
 		if children or self.text:
 			string += ">"
@@ -50,12 +71,18 @@
 		
 		if self.text:
 			string += self.text
+			children.sort()
+			# Note mixing sortable and unsortable lists results in
+			# sort not working, so if you really need your
+			# children to come out in the right order, put them in
+			# a list.  This is only for niceness, where it works.
 		
 		if children:
 			string += "\n"
 				
-		for c in children:
-			string += str(c)
+			for c in children:
+				clines = str(c).rstrip().split("\n")
+				string += "".join(map(lambda l:"  "+l+"\n",clines))
 			
 		if longend:
 			string += "</%s>\n" % name
@@ -64,26 +91,235 @@
 		
 		return string
 
+	
+class BadReply(Exception):
+	pass
+
 class Alias(Reply):
 	def __init__(self, name, meaning):
 		super(Alias,self).__init__()
 		self.name = name
 		self.meaning = meaning
+	
+	def __cmp__(self, other):
+		""" Add __cmp__ to enable comparisons between two Alias objects based upon name."""
+		return cmp(self.name, other.name)
 
 class Config(Reply):
-	def __init__(self, fullname, outputpath):
-		super(Config,self).__init__()
-		self.fullname = fullname
-		self.outputpath = outputpath
+	def __init__(self, raptor, name, text = None):
+		""" Constructor to create a Config from a user-supplied name.
+		possibly including aliases (but not groups)
+		"""
+		super(Config,self).__init__(text, raptor)
+
+		self.query = name
+		
+		# Work out the real name
+		names = name.split(".")
+		if names[0] in self._raptor.cache.aliases:
+			x = self._raptor.cache.FindNamedAlias(names[0])
+			
+			if len(names) > 1:
+				self.meaning = x.meaning + "." + ".".join(names[1:])
+			else:
+				self.meaning = x.meaning
+				
+		elif names[0] in self._raptor.cache.variants:
+			self.meaning = name
+			
+		else:
+			raise BadQuery("'%s' is not an alias or a variant" % names[0])
+		
+	def resolveOutputPath(self):
+		""" Get the outputpath """
+		try:
+			evaluator = self._getEvaluator(self.meaning)
+			# This is messy as some configs construct the path inside the FLM
+			# rather than talking it from the XML: usually because of some
+			# conditional logic... but maybe some refactoring could avoid that.
+			releasepath = evaluator.Get("RELEASEPATH")
+			if not releasepath:
+				raise BadQuery("could not get RELEASEPATH for config '%s'" % self.fullname)
+		
+			variantplatform = evaluator.Get("VARIANTPLATFORM")
+			varianttype = evaluator.Get("VARIANTTYPE")
+			featurevariantname = evaluator.Get("FEATUREVARIANTNAME")
+		
+			platform = evaluator.Get("TRADITIONAL_PLATFORM")
+		
+			if platform == "TOOLS2":
+				self.outputpath = releasepath
+			else:
+				if not variantplatform:
+					raise BadQuery("could not get VARIANTPLATFORM for config '%s'" % self.fullname)
+			
+				if featurevariantname:
+					variantplatform += featurevariantname
+				
+				if not varianttype:
+					raise BadQuery("could not get VARIANTTYPE for config '%s'" % self.fullname)
+			
+				self.outputpath = str(generic_path.Join(releasepath, variantplatform, varianttype))
+		except Exception, e: # Unable to determine output path
+			self.text = str(e)
+
+	def resolveMetadata(self):
+		try:
+			metadata = self.metadata
+		except AttributeError:
+			metadata = MetaData(self.meaning, self._raptor)
+			self.metadata = metadata
+			
+		try:
+			metadata.resolve()
+		except Exception:
+			# Evaluator exception hopefully - already handled
+			self.metadata = None
+
+	def resolveBuild(self):
+		try:
+			build = self.build
+		except AttributeError:
+			build = Build(self.meaning, self._raptor)
+			self.build = build
+			
+		try:
+			build.resolve()
+		except Exception:
+			# Evaluator exception, hopefully - already handled
+			self.build = None
+	
+	def resolveTargettypes(self):
+		try:
+			build = self.build
+		except AttributeError:	
+			build = Build(self.meaning, self._raptor)
+			self.build = build
+		
+		try:
+			build.resolveTargettypes()
+		except Exception:
+			# Evaluator exception hopefully - already handled
+			self.build = None
+
+class MetaData(Reply):
+	def __init__(self, meaning, raptor):
+		super(MetaData,self).__init__("", raptor)
+		self.__meaning = meaning
+
+	def resolve(self):
+		includepaths = []
+		preincludeheader = ""
+		platmacros = []
+
+		evaluator = self._getEvaluator(self.__meaning)
+
+		# Initialise data and metadata objects
+		buildunits = raptor_data.GetBuildUnits([self.__meaning], self._raptor.cache, self._raptor)
+		metareader = raptor_meta.MetaReader(self._raptor, buildunits)
+		metadatafile = raptor_meta.MetaDataFile(generic_path.Path("bld.inf"), "cpp", [], None, self._raptor)
+		
+		# There is only one build platform here; obtain the pre-processing include paths,
+		# OS pre-include file, compiler pre-include file and macros.			
+		includepaths = metadatafile.preparePreProcessorIncludePaths(metareader.BuildPlatforms[0])
+		preincludeheader = metareader.BuildPlatforms[0]['VARIANT_HRH']
+		
+		# Macros arrive as a a list of strings, or a single string, containing definitions of the form "name" or "name=value". 
+		platmacrolist = metadatafile.preparePreProcessorMacros(metareader.BuildPlatforms[0])
+		platmacros.extend(map(lambda macrodef: [macrodef.partition("=")[0], macrodef.partition("=")[2]], platmacrolist))
+
+		# Add child elements to appropriate areas if they were calculated
+		if len(includepaths) > 0:
+			self.includepaths = map(lambda x: Include(str(x)), includepaths)
+		
+		if preincludeheader != "":
+			self.preincludeheader = PreInclude(str(preincludeheader))
+		
+		if len(platmacros):
+			self.platmacros = map(lambda x: Macro(x[0],x[1]) if x[1] else Macro(x[0]), platmacros)
+
+class Build(Reply):
+	def __init__(self, meaning, raptor):
+		super(Build,self).__init__("", raptor)
+		self.__meaning = meaning
+		
+	def resolve(self):
+		compilerpreincludeheader = ""
+		sourcemacros = []
+
+		evaluator = self._getEvaluator(self.__meaning)
+
+		platform = evaluator.Get("TRADITIONAL_PLATFORM")
+			
+		# Compiler preinclude files may or may not be present, depending on the configuration.
+		if evaluator.Get("PREINCLUDE"):
+			compilerpreincludeheader = generic_path.Path(evaluator.Get("PREINCLUDE"))
+			
+		# Macros arrive as a a list of strings, or a single string, containing definitions of the form "name" or "name=value". 
+		# If required, we split to a list, and then processes the constituent parts of the macro.
+		sourcemacrolist = evaluator.Get("CDEFS").split()
+		sourcemacros.extend(map(lambda macrodef: [macrodef.partition("=")[0], macrodef.partition("=")[2]], sourcemacrolist))
+
+		if platform == "TOOLS2":
+			# Source macros are determined in the FLM for tools2 builds, therefore we have to
+			# mimic the logic here
+			if 'win' in raptor.hostplatform or 'win32' in self.__meaning:
+				sourcemacrolist = evaluator.Get("CDEFS.WIN32").split()
+			else:
+				sourcemacrolist = evaluator.Get("CDEFS.LINUX").split()
+			sourcemacros.extend(map(lambda macrodef: [macrodef.partition("=")[0], macrodef.partition("=")[2]], sourcemacrolist))
+
+		if len(sourcemacros):
+			self.sourcemacros = map(lambda x: Macro(x[0],x[1]) if x[1] else Macro(x[0]), sourcemacros)
+			
+		if compilerpreincludeheader:
+			self.compilerpreincludeheader = PreInclude(str(compilerpreincludeheader))
+
+	def resolveTargettypes(self):
+		evaluator = self._getEvaluator(self.__meaning)
+		targettypes = evaluator.Get("TARGET_TYPES").split(' ')
+		self.targettypes = []
+		for type in targettypes:
+			self.targettypes.append(TargetType(type))
+		self.targettypes.sort()	
+
+class TargetType(Reply):
+	def __init__(self, name):
+		super(TargetType,self).__init__()
+		self.name = name
+
+	def __cmp__(self, other):
+		return cmp(self.name, other.name)
 
 class Product(Reply):
 	def __init__(self, name):
 		super(Product,self).__init__()
 		self.name = name
+	
+	def __cmp__(self, other):
+		""" Add __cmp__ to enable comparisons between two Product objects based upon name."""
+		return cmp(self.name, other.name)
+
+class Include(Reply):
+	def __init__(self, path):
+		super(Include,self).__init__()
+		self.path = path
+
+class PreInclude(Reply):
+	def __init__(self, file):
+		super(PreInclude,self).__init__()
+		self.file = file
+
+class Macro(Reply):
+	def __init__(self, name, value=None):
+		super(Macro,self).__init__()
+		self.name = name
+		self.value = value
 
 import generic_path
 import raptor
 import raptor_data
+import raptor_meta
 import re
 
 class Context(object):
@@ -141,7 +377,7 @@
 			if type == ALL or a.type == type:
 				# copy the members we want to expose
 				aliases.append( Alias(a.name, a.meaning) )
-			
+		aliases.sort()	
 		return aliases
 	
 	def getconfig(self, name):
@@ -151,57 +387,13 @@
 		dot-separated list of variants. For example "armv5_urel" or
 		"armv5_urel.savespace.vasco".
 		"""
-		names = name.split(".")
-		if names[0] in self.__raptor.cache.aliases:
-			x = self.__raptor.cache.FindNamedAlias(names[0])
-			
-			if len(names) > 1:
-				fullname = x.meaning + "." + ".".join(names[1:])
-			else:
-				fullname = x.meaning
-				
-		elif names[0] in self.__raptor.cache.variants:
-			fullname = name
-			
-		else:
-			raise BadQuery("'%s' is not an alias or a variant" % names[0])
-		
-		# create an evaluator for the named configuration
-		tmp = raptor_data.Alias("tmp")
-		tmp.SetProperty("meaning", fullname)
-		
-		units = tmp.GenerateBuildUnits(self.__raptor.cache)
-		evaluator = self.__raptor.GetEvaluator(None, units[0])
-		
-		# get the outputpath
-		# this is messy as some configs construct the path inside the FLM
-		# rather than talking it from the XML: usually because of some
-		# conditional logic... but maybe some refactoring could avoid that.
-		releasepath = evaluator.Get("RELEASEPATH")
-		if not releasepath:
-			raise BadQuery("could not get RELEASEPATH for config '%s'" % name)
-		
-		variantplatform = evaluator.Get("VARIANTPLATFORM")
-		varianttype = evaluator.Get("VARIANTTYPE")
-		featurevariantname = evaluator.Get("FEATUREVARIANTNAME")
-		
-		platform = evaluator.Get("TRADITIONAL_PLATFORM")
-		
-		if platform == "TOOLS2":
-			outputpath = releasepath
-		else:
-			if not variantplatform:
-				raise BadQuery("could not get VARIANTPLATFORM for config '%s'" % name)
-			
-			if featurevariantname:
-				variantplatform += featurevariantname
-				
-			if not varianttype:
-				raise BadQuery("could not get VARIANTTYPE for config '%s'" % name)
-			
-			outputpath = str(generic_path.Join(releasepath, variantplatform, varianttype))
-		
-		return Config(fullname, outputpath)
+
+		config = Config(self.__raptor, name)
+		config.resolveOutputPath()
+		config.resolveTargettypes()
+		config.resolveMetadata()
+		config.resolveBuild()
+		return config		
 		
 	def getproducts(self):
 		"""extract all product variants."""
@@ -212,7 +404,7 @@
 			if v.type == "product":
 				# copy the members we want to expose
 				variants.append( Product(v.name) )
-			
+		variants.sort()	
 		return variants
 	
 class BadQuery(Exception):