diff -r f70b728ea30c -r a1925fb7753a sbsv2/raptor/python/raptor_api.py --- 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 += "\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):