sbsv2/raptor/python/raptor_data.py
changeset 0 044383f39525
child 3 e1eecf4d390d
child 590 360bd6b35136
equal deleted inserted replaced
-1:000000000000 0:044383f39525
       
     1 #
       
     2 # Copyright (c) 2006-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_data module
       
    16 # This module contains the classes that make up the Raptor Data Model.
       
    17 #
       
    18 
       
    19 import copy
       
    20 import generic_path
       
    21 import os
       
    22 import hashlib
       
    23 import raptor_utilities
       
    24 import re
       
    25 import types
       
    26 import sys
       
    27 import subprocess
       
    28 from tempfile import gettempdir
       
    29 from time import time, clock
       
    30 
       
    31 
       
    32 
       
    33 # What host platforms we recognise
       
    34 # This allows us to tie some variants to one host platform and some to another
       
    35 class HostPlatform(object):
       
    36 	""" List the host platforms on which we can build.  Allow configuration
       
    37  	    files to specify different information based on that.
       
    38 	"""
       
    39 	hostplatforms = ["win32", "win64", "linux2"]
       
    40 	hostplatform = sys.platform
       
    41 
       
    42 	@classmethod
       
    43 	def IsKnown(cls, platformpattern):
       
    44 		"Does the parameter match the name of a known platform "
       
    45 		hpnre = re.compile(platformpattern, re.I)
       
    46 		for hp in cls.hostplatforms:
       
    47 			if hpnre.match(hp):
       
    48 				return True
       
    49 		return False
       
    50 
       
    51 	@classmethod
       
    52 	def IsHost(cls, platformpattern):
       
    53 		""" Does the parameter match the name of the
       
    54 		    platform that we're executing on? """
       
    55 		ppre = re.compile(platformpattern, re.I)
       
    56 		if ppre.match(cls.hostplatform):
       
    57 			return True
       
    58 		return False
       
    59 
       
    60 
       
    61 # Make sure not to start up on an unsupported platform
       
    62 if not HostPlatform.IsKnown(HostPlatform.hostplatform):
       
    63 	raise Exception("raptor_data module loaded on an unrecognised platform '%s'. Expected one of %s" % (hostplatform, str(hostplatforms)))
       
    64 
       
    65 
       
    66 # raptor_data module classes
       
    67 
       
    68 class Model(object):
       
    69 	"Base class for data-model objects"
       
    70 
       
    71 	def __init__(self):
       
    72 		self.owner = None	# Raptor object
       
    73 		self.source = None	# XML file
       
    74 		self.indent = " "	# for DebugPrint
       
    75 		self.host = None
       
    76 		self.cacheID = ""	# default set of cached objects
       
    77 
       
    78 
       
    79 	def SetOwner(self, aRaptor):
       
    80 		self.owner = aRaptor
       
    81 
       
    82 
       
    83 	def SetSourceFile(self, filename):
       
    84 		self.source = filename
       
    85 
       
    86 
       
    87 	def SetProperty(self, name, value):
       
    88 		raise InvalidPropertyError()
       
    89 
       
    90 
       
    91 	def AddChild(self, child):
       
    92 		raise InvalidChildError()
       
    93 
       
    94 
       
    95 	def DebugPrint(self, prefix = ""):
       
    96 		if self.owner:
       
    97 			self.owner.Debug("%s", prefix)
       
    98 
       
    99 	def Valid(self):
       
   100 		return False
       
   101 
       
   102 	def IsApplicable(self):
       
   103 		"This variant may be caused to only apply when used on a particular host build platform"
       
   104 		if self.host is None:
       
   105 			return True
       
   106 
       
   107 		if HostPlatform.IsHost(self.host):
       
   108 			return True
       
   109 
       
   110 		return False
       
   111 
       
   112 
       
   113 class InvalidPropertyError(Exception):
       
   114 	pass
       
   115 
       
   116 class InvalidChildError(Exception):
       
   117 	pass
       
   118 
       
   119 class BadReferenceError(Exception):
       
   120 	pass
       
   121 
       
   122 
       
   123 class Reference(Model):
       
   124 	"Base class for data-model reference objects"
       
   125 
       
   126 	def __init__(self, ref = None):
       
   127 		Model.__init__(self)
       
   128 		self.ref = ref
       
   129 		self.modifiers = []
       
   130 
       
   131 	def SetProperty(self, name, value):
       
   132 		if name == "ref":
       
   133 			self.ref = value
       
   134 		elif name == "mod":
       
   135 			self.modifiers = value.split(".")
       
   136 		else:
       
   137 			raise InvalidPropertyError()
       
   138 
       
   139 	def Resolve(self):
       
   140 		raise BadReferenceError()
       
   141 
       
   142 	def GetModifiers(self):
       
   143 		cache = self.owner.cache
       
   144 		return [ cache.FindNamedVariant(m) for m in self.modifiers ]
       
   145 
       
   146 	def Valid(self):
       
   147 		return self.ref
       
   148 
       
   149 
       
   150 class VariantContainer(Model):
       
   151 
       
   152 	def __init__(self):
       
   153 		Model.__init__(self)	# base class constructor
       
   154 		self.variants = []
       
   155 
       
   156 
       
   157 	def SetOwner(self, aRaptor):
       
   158 		Model.SetOwner(self, aRaptor)
       
   159 		for v in self.variants:
       
   160 			v.SetOwner(aRaptor)
       
   161 
       
   162 
       
   163 	def DebugPrint(self, prefix = ""):
       
   164 		for v in self.variants:
       
   165 			v.DebugPrint(prefix)
       
   166 
       
   167 
       
   168 	def AddVariant(self, variant):
       
   169 		if type(variant) is types.StringTypes:
       
   170 			variant = VariantRef(variant)
       
   171 
       
   172 
       
   173 		# Only add the variant if it's not in the list
       
   174 		# already
       
   175 		if not variant in self.variants:
       
   176 			self.variants.append(variant)
       
   177 
       
   178 	def GetVariants(self):
       
   179 		# resolve any VariantRef objects into Variant objects
       
   180 		for i,var in enumerate(self.variants):
       
   181 			if isinstance(var, Reference):
       
   182 				try:
       
   183 					self.variants[i] = var.Resolve()
       
   184 
       
   185 				except BadReferenceError:
       
   186 					self.owner.Error("Missing variant '%s'", var.ref)
       
   187 
       
   188 		return self.variants
       
   189 
       
   190 
       
   191 class Interface(Model):
       
   192 
       
   193 	def __init__(self, name = None):
       
   194 		Model.__init__(self)	# base class constructor
       
   195 		self.name = name
       
   196 		self.flm = None
       
   197 		self.abstract = False
       
   198 		self.extends = None
       
   199 		self.params = []
       
   200 		self.paramgroups = []
       
   201 
       
   202 	def DebugPrint(self, prefix = ""):
       
   203 		self.owner.Debug("%s<interface name='%s'>", prefix, self.name)
       
   204 		self.owner.Debug("%s...", prefix)
       
   205 		self.owner.Debug("%s</interface>", prefix)
       
   206 
       
   207 	def FindParent(self):
       
   208 		try:
       
   209 			return self.owner.cache.FindNamedInterface(self.extends, self.cacheID)
       
   210 		except KeyError:
       
   211 			raise BadReferenceError("Cannot extend interface because it cannot be found: "+str(self.extends))
       
   212 
       
   213 	def GetParams(self):
       
   214 		if self.extends != None:
       
   215 			parent = self.FindParent()
       
   216 
       
   217 			# what parameter names do we have already?
       
   218 			names = set([x.name for x in self.params])
       
   219 
       
   220 			# pick up ones we don't have that are in our parent
       
   221 			pp = []
       
   222 			for p in parent.GetParams():
       
   223 				if not p.name in names:
       
   224 					pp.append(p)
       
   225 
       
   226 			# list parent parameters first then ours
       
   227 			pp.extend(self.params)
       
   228 			return pp
       
   229 
       
   230 		return self.params
       
   231 
       
   232 	def GetParamGroups(self):
       
   233 		if self.extends != None:
       
   234 			parent = self.FindParent()
       
   235 
       
   236 			# what parameter names do we have already?
       
   237 			patterns = set([x.pattern for x in self.paramgroups])
       
   238 
       
   239 			# pick up ones we don't have that are in our parent
       
   240 			for g in parent.GetParamGroups():
       
   241 				if not g.pattern in patterns:
       
   242 					self.paramgroups.append(g)
       
   243 
       
   244 		return self.paramgroups
       
   245 
       
   246 
       
   247 	def GetFLMIncludePath(self):
       
   248 		"absolute path to the FLM"
       
   249 
       
   250 		if self.flm == None:
       
   251 			if self.extends != None:
       
   252 				parent = self.FindParent()
       
   253 
       
   254 				return parent.GetFLMIncludePath()
       
   255 			else:
       
   256 				raise InvalidPropertyError()
       
   257 
       
   258 		if not os.path.isabs(self.flm):
       
   259 			self.flm = os.path.join(os.path.dirname(self.source), self.flm)
       
   260 
       
   261 		return generic_path.Path(self.flm)
       
   262 
       
   263 
       
   264 	def SetProperty(self, name, value):
       
   265 		if name == "name":
       
   266 			self.name = value
       
   267 		elif name == "flm":
       
   268 			self.flm = value
       
   269 		elif name == "abstract":
       
   270 			self.abstract = (value == "true")
       
   271 		elif name == "extends":
       
   272 			self.extends = value
       
   273 		else:
       
   274 			raise InvalidPropertyError()
       
   275 
       
   276 
       
   277 	def AddChild(self, child):
       
   278 		if isinstance(child, Parameter):
       
   279 			self.AddParameter(child)
       
   280 		elif isinstance(child, ParameterGroup):
       
   281 			self.AddParameterGroup(child)
       
   282 		else:
       
   283 			raise InvalidChildError()
       
   284 
       
   285 
       
   286 	def AddParameter(self, parameter):
       
   287 		self.params.append(parameter)
       
   288 
       
   289 	def AddParameterGroup(self, parametergroup):
       
   290 		self.paramgroups.append(parametergroup)
       
   291 
       
   292 	def Valid(self):
       
   293 		return (self.name != None)
       
   294 
       
   295 
       
   296 class InterfaceRef(Reference):
       
   297 
       
   298 	def DebugPrint(self, prefix = ""):
       
   299 		self.owner.Debug("%s<interfaceRef ref='%s'/>", prefix, self.ref)
       
   300 
       
   301 	def Resolve(self):
       
   302 		try:
       
   303 			return self.owner.cache.FindNamedInterface(self.ref, self.cacheID)
       
   304 		except KeyError:
       
   305 			raise BadReferenceError()
       
   306 
       
   307 
       
   308 class Specification(VariantContainer):
       
   309 
       
   310 	def __init__(self, name = "", type = ""):
       
   311 		VariantContainer.__init__(self)	# base class constructor
       
   312 		self.name = name
       
   313 		self.type = type
       
   314 		self.interface = None
       
   315 		self.childSpecs = []
       
   316 		self.parentSpec = None
       
   317 
       
   318 
       
   319 	def DebugPrint(self, prefix = ""):
       
   320 		self.owner.Debug("%s<spec name='%s'>", prefix, self.name)
       
   321 		if self.interface:
       
   322 			self.interface.DebugPrint(prefix + self.indent)
       
   323 		VariantContainer.DebugPrint(self, prefix + self.indent)
       
   324 		for c in self.childSpecs:
       
   325 			c.DebugPrint(prefix + self.indent)
       
   326 		self.owner.Debug("%s</spec>", prefix)
       
   327 
       
   328 
       
   329 	def SetOwner(self, aRaptor):
       
   330 		VariantContainer.SetOwner(self, aRaptor)
       
   331 
       
   332 		if self.interface != None:
       
   333 			self.interface.SetOwner(aRaptor)
       
   334 
       
   335 		for s in self.childSpecs:
       
   336 			s.SetOwner(aRaptor)
       
   337 
       
   338 
       
   339 	def SetProperty(self, name, value):
       
   340 		if name == "name":
       
   341 			self.name = value
       
   342 		else:
       
   343 			raise InvalidPropertyError()
       
   344 
       
   345 
       
   346 	def Configure(self, config):
       
   347 		# configure all the children (some may be Filters or parents of)
       
   348 		for spec in self.GetChildSpecs():
       
   349 			spec.Configure(config)
       
   350 
       
   351 
       
   352 	def HasInterface(self):
       
   353 		return self.interface != None
       
   354 
       
   355 
       
   356 	def SetInterface(self, interface):
       
   357 		if isinstance(interface, Interface) \
       
   358 		or isinstance(interface, InterfaceRef):
       
   359 			self.interface = interface
       
   360 		else:
       
   361 			self.interface = InterfaceRef(interface)
       
   362 
       
   363 
       
   364 	def GetInterface(self):
       
   365 		"""return the Interface (fetching from the cache if it was a ref)
       
   366 		may return None"""
       
   367 
       
   368 		if self.interface == None \
       
   369 		or isinstance(self.interface, Interface):
       
   370 			return self.interface
       
   371 
       
   372 		if isinstance(self.interface, InterfaceRef):
       
   373 			try:
       
   374 				self.interface = self.interface.Resolve()
       
   375 				return self.interface
       
   376 
       
   377 			except BadReferenceError:
       
   378 				self.owner.Error("Missing interface %s", self.interface.ref)
       
   379 				return None
       
   380 
       
   381 
       
   382 	def AddChild(self, child):
       
   383 		if isinstance(child, Specification):
       
   384 			self.AddChildSpecification(child)
       
   385 		elif isinstance(child, Interface) \
       
   386 		  or isinstance(child, InterfaceRef):
       
   387 			self.SetInterface(child)
       
   388 		elif isinstance(child, Variant) \
       
   389 		  or isinstance(child, VariantRef):
       
   390 			self.AddVariant(child)
       
   391 		else:
       
   392 			raise InvalidChildError()
       
   393 
       
   394 
       
   395 	def AddChildSpecification(self, child):
       
   396 		child.SetParentSpec(self)
       
   397 		self.childSpecs.append(child)
       
   398 
       
   399 
       
   400 	def SetParentSpec(self, parent):
       
   401 		self.parentSpec = parent
       
   402 
       
   403 
       
   404 	def GetChildSpecs(self):
       
   405 		return self.childSpecs
       
   406 
       
   407 
       
   408 	def Valid(self):
       
   409 		return True
       
   410 
       
   411 
       
   412 	def GetAllVariantsRecursively(self):
       
   413 		"""Returns all variants contained in this node and in its ancestors.
       
   414 
       
   415 		The returned value is a list, the structure of which is [variants-in-parent,
       
   416 		variants-in-self].
       
   417 
       
   418 		Note that the function recurses through parent *Specifications*, not through
       
   419 		the variants themselves.
       
   420 		"""
       
   421 		if self.parentSpec:
       
   422 			variants = self.parentSpec.GetAllVariantsRecursively()
       
   423 		else:
       
   424 			variants = []
       
   425 
       
   426 		variants.extend( self.GetVariants() )
       
   427 
       
   428 		return variants
       
   429 
       
   430 
       
   431 class Filter(Specification):
       
   432 	"""A Filter is two Specification nodes and a True/False switch.
       
   433 
       
   434 	Filter extends Specification to have two nodes, only one of
       
   435 	which can be active at any time. Which node is active is determined
       
   436 	when the Configure method is called after setting up a Condition.
       
   437 
       
   438 	If several Conditions are set, the test is an OR of all of them."""
       
   439 
       
   440 	def __init__(self, name = ""):
       
   441 		Specification.__init__(self, name)	# base class constructor
       
   442 		self.Else = Specification(name)     # same for Else part
       
   443 		self.isTrue = True
       
   444 		self.configNames = set()            #
       
   445 		self.variableNames = set()          # TO DO: Condition class
       
   446 		self.variableValues = {}            #
       
   447 
       
   448 	def DebugPrint(self, prefix = ""):
       
   449 		self.owner.Debug("%s<filter name='%s'>", prefix, self.name)
       
   450 		self.owner.Debug("%s <if config='%s'>", prefix, " | ".join(self.configNames))
       
   451 		Specification.DebugPrint(self, prefix + self.indent)
       
   452 		self.owner.Debug("%s </if>", prefix)
       
   453 		self.owner.Debug("%s <else>", prefix)
       
   454 		self.Else.DebugPrint(prefix + self.indent)
       
   455 		self.owner.Debug("%s </else>", prefix)
       
   456 		self.owner.Debug("%s</filter>", prefix)
       
   457 
       
   458 
       
   459 	def SetConfigCondition(self, configName):
       
   460 		self.configNames = set([configName])
       
   461 
       
   462 	def AddConfigCondition(self, configName):
       
   463 		self.configNames.add(configName)
       
   464 
       
   465 
       
   466 	def SetVariableCondition(self, variableName, variableValues):
       
   467 		self.variableNames = set([variableName])
       
   468 		if type(variableValues) == types.ListType:
       
   469 			self.variableValues[variableName] = set(variableValues)
       
   470 		else:
       
   471 			self.variableValues[variableName] = set([variableValues])
       
   472 
       
   473 	def AddVariableCondition(self, variableName, variableValues):
       
   474 		self.variableNames.add(variableName)
       
   475 		if type(variableValues) == types.ListType:
       
   476 			self.variableValues[variableName] = set(variableValues)
       
   477 		else:
       
   478 			self.variableValues[variableName] = set([variableValues])
       
   479 
       
   480 
       
   481 	def Configure(self, buildUnit):
       
   482 		self.isTrue = False
       
   483 
       
   484 		if buildUnit.name in self.configNames:
       
   485 			self.isTrue = True
       
   486 		elif self.variableNames:
       
   487 			evaluator = self.owner.GetEvaluator(self.parentSpec, buildUnit)
       
   488 
       
   489 			for variableName in self.variableNames:
       
   490 				variableValue = evaluator.Get(variableName)
       
   491 
       
   492 				if variableValue in self.variableValues[variableName]:
       
   493 					self.isTrue = True
       
   494 					break
       
   495 
       
   496 		# configure all the children too
       
   497 		for spec in self.GetChildSpecs():
       
   498 			spec.Configure(buildUnit)
       
   499 
       
   500 
       
   501 	def SetOwner(self, aRaptor):
       
   502 		# base class method
       
   503 		Specification.SetOwner(self, aRaptor)
       
   504 		# same for Else part
       
   505 		self.Else.SetOwner(aRaptor)
       
   506 
       
   507 
       
   508 	def HasInterface(self):
       
   509 		if self.isTrue:
       
   510 			return Specification.HasInterface(self)
       
   511 		else:
       
   512 			return self.Else.HasInterface()
       
   513 
       
   514 
       
   515 	def GetInterface(self):
       
   516 		if self.isTrue:
       
   517 			return Specification.GetInterface(self)
       
   518 		else:
       
   519 			return self.Else.GetInterface()
       
   520 
       
   521 
       
   522 	def GetVariants(self):
       
   523 		if self.isTrue:
       
   524 			return Specification.GetVariants(self)
       
   525 		else:
       
   526 			return self.Else.GetVariants()
       
   527 
       
   528 
       
   529 	def SetParentSpec(self, parent):
       
   530 		# base class method
       
   531 		Specification.SetParentSpec(self, parent)
       
   532 		# same for Else part
       
   533 		self.Else.SetParentSpec(parent)
       
   534 
       
   535 
       
   536 	def GetChildSpecs(self):
       
   537 		if self.isTrue:
       
   538 			return Specification.GetChildSpecs(self)
       
   539 		else:
       
   540 			return self.Else.GetChildSpecs()
       
   541 
       
   542 
       
   543 class Parameter(Model):
       
   544 
       
   545 	def __init__(self, name = None, default = None):
       
   546 		Model.__init__(self)	# base class constructor
       
   547 		self.name = name
       
   548 		self.default = default
       
   549 
       
   550 
       
   551 	def SetProperty(self, name, value):
       
   552 		if name == "name":
       
   553 			self.name = value
       
   554 		elif name == "default":
       
   555 			self.default = value
       
   556 		else:
       
   557 			raise InvalidPropertyError()
       
   558 
       
   559 
       
   560 	def Valid(self):
       
   561 		return (self.name != None)
       
   562 
       
   563 class ParameterGroup(Model):
       
   564 	"""A group of Parameters specified in an interface by a regexp"""
       
   565 	def __init__(self, pattern = None, default = None):
       
   566 		Model.__init__(self)	# base class constructor
       
   567 		self.pattern = pattern
       
   568 
       
   569 		self.patternre = None
       
   570 		if pattern:
       
   571 			try:
       
   572 				self.patternre = re.compile(pattern)
       
   573 			except TypeError:
       
   574 				pass
       
   575 		self.default = default
       
   576 
       
   577 
       
   578 	def SetProperty(self, pattern, value):
       
   579 		if pattern == "pattern":
       
   580 			self.pattern = value
       
   581 			self.patternre = re.compile(value)
       
   582 		elif pattern == "default":
       
   583 			self.default = value
       
   584 		else:
       
   585 			raise InvalidPropertyError()
       
   586 
       
   587 
       
   588 	def Valid(self):
       
   589 		return (self.pattern != None and self.patternre != None)
       
   590 
       
   591 
       
   592 class Operation(Model):
       
   593 	"Base class for variant operations"
       
   594 	def __init__(self):
       
   595 		Model.__init__(self)	# base class constructor
       
   596 		self.type = None
       
   597 
       
   598 
       
   599 	def Apply(self, oldValue):
       
   600 		pass
       
   601 
       
   602 
       
   603 class Append(Operation):
       
   604 	__slots__ = ('name','value','separator','owner')
       
   605 
       
   606 	def __init__(self, name = None, value = None, separator = " "):
       
   607 		Operation.__init__(self)	# base class constructor
       
   608 		self.name = name
       
   609 		self.value = value
       
   610 		self.separator = separator
       
   611 
       
   612 
       
   613 	def DebugPrint(self, prefix = ""):
       
   614 		attributes = "name='" + self.name + "' value='" + self.value + "' separator='" + self.separator + "'"
       
   615 		self.owner.Debug("%s<append %s/>", prefix, attributes)
       
   616 
       
   617 
       
   618 	def Apply(self, oldValue):
       
   619 		if len(oldValue) > 0:
       
   620 			if len(self.value) > 0:
       
   621 				return oldValue + self.separator + self.value
       
   622 			else:
       
   623 				return oldValue
       
   624 		else:
       
   625 			return self.value
       
   626 
       
   627 
       
   628 	def SetProperty(self, name, value):
       
   629 		if name == "name":
       
   630 			self.name = value
       
   631 		elif name == "value":
       
   632 			self.value = value
       
   633 		elif name == "separator":
       
   634 			self.separator = value
       
   635 		else:
       
   636 			raise InvalidPropertyError()
       
   637 
       
   638 
       
   639 	def Valid(self):
       
   640 		return (self.name != None and self.value != None)
       
   641 
       
   642 
       
   643 class Prepend(Operation):
       
   644 	def __init__(self, name = None, value = None, separator = " "):
       
   645 		Operation.__init__(self)	# base class constructor
       
   646 		self.name = name
       
   647 		self.value = value
       
   648 		self.separator = separator
       
   649 
       
   650 
       
   651 	def DebugPrint(self, prefix = ""):
       
   652 		attributes = "name='" + self.name + "' value='" + self.value + "' separator='" + self.separator + "'"
       
   653 		self.owner.Debug("%s<prepend %s/>", prefix, attributes)
       
   654 
       
   655 
       
   656 	def Apply(self, oldValue):
       
   657 		if len(oldValue) > 0:
       
   658 			if len(self.value) > 0:
       
   659 				return self.value + self.separator + oldValue
       
   660 			else:
       
   661 				return oldValue
       
   662 		else:
       
   663 			return self.value
       
   664 
       
   665 
       
   666 	def SetProperty(self, name, value):
       
   667 		if name == "name":
       
   668 			self.name = value
       
   669 		elif name == "value":
       
   670 			self.value = value
       
   671 		elif name == "separator":
       
   672 			self.separator = value
       
   673 		else:
       
   674 			raise InvalidPropertyError()
       
   675 
       
   676 
       
   677 	def Valid(self):
       
   678 		return (self.name != None and self.value != None)
       
   679 
       
   680 
       
   681 class Set(Operation):
       
   682 	"""implementation of <set> operation"""
       
   683 	__slots__ = ('name','value', 'type', 'versionCommand', 'versionResult', 'owner')
       
   684 
       
   685 	def __init__(self, name = None, value = "", type = ""):
       
   686 		Operation.__init__(self)	# base class constructor
       
   687 		self.name = name
       
   688 		self.value = value
       
   689 		self.type = type
       
   690 		self.versionCommand = ""
       
   691 		self.versionResult = ""
       
   692 
       
   693 
       
   694 	def DebugPrint(self, prefix = ""):
       
   695 		attributes = "name='" + self.name + "' value='" + self.value + "' type='" + self.type + "'"
       
   696 		if type == "tool":
       
   697 			attributes += " versionCommand='" + self.versionCommand + "' versionResult='" + self.versionResult
       
   698 
       
   699 		self.owner.Debug("%s<set %s/>", prefix, attributes)
       
   700 
       
   701 
       
   702 	def Apply(self, oldValue):
       
   703 		return self.value
       
   704 
       
   705 
       
   706 	def SetProperty(self, name, value):
       
   707 		if name == "name":
       
   708 			self.name = value
       
   709 		elif name == "value":
       
   710 			self.value = value
       
   711 		elif name == "type":
       
   712 			self.type = value
       
   713 		elif name == "versionCommand":
       
   714 			self.versionCommand = value
       
   715 		elif name == "versionResult":
       
   716 			self.versionResult = value
       
   717 		elif name == "host":
       
   718 			if HostPlatform.IsKnown(value):
       
   719 				self.host = value
       
   720 		else:
       
   721 			raise InvalidPropertyError()
       
   722 
       
   723 
       
   724 	def Valid(self):
       
   725 		return (self.name != None and self.value != None)
       
   726 
       
   727 
       
   728 class Env(Set):
       
   729 	"""implementation of <env> operator"""
       
   730 
       
   731 	def __init__(self, name = None, default = None, type = ""):
       
   732 		Set.__init__(self, name, "", type)	# base class constructor
       
   733 		self.default = default
       
   734 
       
   735 
       
   736 	def DebugPrint(self, prefix = ""):
       
   737 		attributes = "name='" + self.name + "' type='" + self.type + "'"
       
   738 		if default != None:
       
   739 			attributes += " default='" + self.default + "'"
       
   740 
       
   741 		if type == "tool":
       
   742 			attributes += " versionCommand='" + self.versionCommand + "' versionResult='" + self.versionResult + "'"
       
   743 
       
   744 		self.owner.Debug("%s<env %s/>", prefix, attributes)
       
   745 
       
   746 
       
   747 	def Apply(self, oldValue):
       
   748 		try:
       
   749 			value = os.environ[self.name]
       
   750 
       
   751 			# if this value is a "path" or a "tool" then we need to make sure
       
   752 			# it is a proper absolute path in our preferred format.
       
   753 			if value and (self.type == "path" or self.type == "tool"):
       
   754 				try:
       
   755 					path = generic_path.Path(value)
       
   756 					value = str(path.Absolute())
       
   757 				except ValueError,e:
       
   758 					self.owner.Error("the environment variable %s is incorrect: %s" % (self.name, str(e)))
       
   759 					return "NO_VALUE_FOR_" + self.name
       
   760 		except KeyError:
       
   761 			if self.default != None:
       
   762 				value = self.default
       
   763 			else:
       
   764 				self.owner.Error("%s is not set in the environment and has no default", self.name)
       
   765 				return "NO_VALUE_FOR_" + self.name
       
   766 
       
   767 		return value
       
   768 
       
   769 
       
   770 	def SetProperty(self, name, value):
       
   771 		if name == "default":
       
   772 			self.default = value
       
   773 		else:
       
   774 			Set.SetProperty(self, name, value)
       
   775 
       
   776 
       
   777 	def Valid(self):
       
   778 		return (self.name != None)
       
   779 
       
   780 
       
   781 class BuildUnit(object):
       
   782 	"Represents an individual buildable unit."
       
   783 
       
   784 	def __init__(self, name, variants):
       
   785 		self.name = name
       
   786 		
       
   787 		# A list of Variant objects.
       
   788 		self.variants = variants
       
   789 
       
   790 		# Cache for the variant operations implied by this BuildUnit.
       
   791 		self.operations = []
       
   792 		self.variantKey = ""
       
   793 
       
   794 	def GetOperations(self):
       
   795 		"""Return all operations related to this BuildUnit.
       
   796 		
       
   797 		The result is cached, and so will only be computed once per BuildUnit.
       
   798 		"""
       
   799 		key = '.'.join([x.name for x in self.variants])
       
   800 		if self.variantKey != key:
       
   801 			self.variantKey = key
       
   802 			for v in self.variants:
       
   803 				self.operations.extend( v.GetAllOperationsRecursively() )
       
   804 
       
   805 		return self.operations
       
   806 
       
   807 class Config(object):
       
   808 	"""Abstract type representing an argument to the '-c' option.
       
   809 
       
   810 	The fundamental property of a Config is that it can generate one or more
       
   811 	BuildUnits.
       
   812 	"""
       
   813 
       
   814 	def __init__(self):
       
   815 		self.modifiers = []
       
   816 
       
   817 	def AddModifier(self, variant):
       
   818 		self.modifiers.append(variant)
       
   819 
       
   820 	def ClearModifiers(self):
       
   821 		self.modifiers = []
       
   822 
       
   823 	def GenerateBuildUnits(self):
       
   824 		"""Returns a list of BuildUnits.
       
   825 
       
   826 		This function must be overridden by derived classes.
       
   827 		"""
       
   828 		raise NotImplementedError()
       
   829 
       
   830 
       
   831 class Variant(Model, Config):
       
   832 
       
   833 	def __init__(self, name = ""):
       
   834 		Model.__init__(self)
       
   835 		Config.__init__(self)
       
   836 		self.name = name
       
   837 
       
   838 		# Operations defined inside this variant.
       
   839 		self.ops = []
       
   840 
       
   841 		# The name of our parent variant, if any.
       
   842 		self.extends = ""
       
   843 
       
   844 		# Any variant references used inside this variant.
       
   845 		self.variantRefs = []
       
   846 
       
   847 		self.allOperations = []
       
   848 
       
   849 	def SetProperty(self, name, value):
       
   850 		if name == "name":
       
   851 			self.name = value
       
   852 		elif name == "host":
       
   853 			if HostPlatform.IsKnown(value):
       
   854 				self.host = value
       
   855 		elif name == "extends":
       
   856 			self.extends = value
       
   857 		else:
       
   858 			raise InvalidPropertyError()
       
   859 
       
   860 	def AddChild(self, child):
       
   861 		if isinstance(child, Operation):
       
   862 			self.ops.append(child)
       
   863 		elif isinstance(child, VariantRef):
       
   864 			self.variantRefs.append(child)
       
   865 		else:
       
   866 			raise InvalidChildError()
       
   867 
       
   868 	def Valid(self):
       
   869 		return self.name
       
   870 
       
   871 	def SetOwner(self, aRaptor):
       
   872 
       
   873 		Model.SetOwner(self, aRaptor)
       
   874 
       
   875 		for r in self.variantRefs:
       
   876 			r.SetOwner(aRaptor)
       
   877 
       
   878 		for op in self.ops:
       
   879 			op.SetOwner(aRaptor)
       
   880 
       
   881 	def AddOperation(self, op):
       
   882 		self.ops.append(op)
       
   883 
       
   884 	def GetAllOperationsRecursively(self):
       
   885 		"""Returns a list of all operations in this variant.
       
   886 
       
   887 		The list elements are themselves lists; the overall structure of the
       
   888 		returned value is:
       
   889 
       
   890 		[ [ops-from-parent],[ops-from-varRefs], [ops-in-self] ]
       
   891 		"""
       
   892 
       
   893 		if not self.allOperations:
       
   894 			if self.extends:
       
   895 				parent = self.owner.cache.FindNamedVariant(self.extends)
       
   896 				self.allOperations.extend( parent.GetAllOperationsRecursively() )
       
   897 			for r in self.variantRefs:
       
   898 				for v in [ r.Resolve() ] + r.GetModifiers():
       
   899 					self.allOperations.extend( v.GetAllOperationsRecursively() )
       
   900 			self.allOperations.append(self.ops)
       
   901 
       
   902 		return self.allOperations
       
   903 
       
   904 	def GenerateBuildUnits(self):
       
   905 
       
   906 		name = self.name
       
   907 		vars = [self]
       
   908 
       
   909 		for m in self.modifiers:
       
   910 			name = name + "." + m.name
       
   911 			vars.append(m)
       
   912 
       
   913 		return [ BuildUnit(name, vars) ]
       
   914 
       
   915 	def DebugPrint(self, prefix = ""):
       
   916 
       
   917 		self.owner.Debug("%s<var name='%s' extends='%s'>", prefix, self.name, self.extends)
       
   918 		for op in self.ops:
       
   919 			op.DebugPrint(prefix + self.indent)
       
   920 
       
   921 		self.owner.Debug("%s</var>", prefix)
       
   922 
       
   923 
       
   924 class VariantRef(Reference):
       
   925 
       
   926 	def __init__(self, ref=None):
       
   927 		Reference.__init__(self, ref)
       
   928 
       
   929 	def DebugPrint(self, prefix = ""):
       
   930 		self.owner.Debug("%s<varRef ref='%s'/>", prefix, self.ref)
       
   931 
       
   932 	def Resolve(self):
       
   933 		try:
       
   934 			return self.owner.cache.FindNamedVariant(self.ref)
       
   935 		except KeyError:
       
   936 			raise BadReferenceError(self.ref)
       
   937 
       
   938 
       
   939 class Alias(Model, Config):
       
   940 
       
   941 	def __init__(self, name=""):
       
   942 		Model.__init__(self)
       
   943 		Config.__init__(self)
       
   944 		self.name = name
       
   945 		self.meaning = ""
       
   946 		self.varRefs = []
       
   947 		self.variants = []
       
   948 
       
   949 	def DebugPrint(self, prefix = ""):
       
   950 		self.owner.Debug("%s<alias name='%s' meaning='%s'/>", prefix, self.name, self.meaning)
       
   951 
       
   952 	def SetProperty(self, key, val):
       
   953 		if key == "name":
       
   954 			self.name = val
       
   955 		elif key == "meaning":
       
   956 			self.meaning = val
       
   957 
       
   958 			for u in val.split("."):
       
   959 				self.varRefs.append( VariantRef(u) )
       
   960 		else:
       
   961 			raise InvalidPropertyError()
       
   962 
       
   963 	def SetOwner(self, raptor):
       
   964 		Model.SetOwner(self, raptor)
       
   965 		for r in self.varRefs:
       
   966 			r.SetOwner(raptor)
       
   967 
       
   968 	def Valid(self):
       
   969 		return self.name and self.meaning
       
   970 
       
   971 	def GenerateBuildUnits(self):
       
   972 		if not self.variants:
       
   973 			for r in self.varRefs:
       
   974 				try:
       
   975 					self.variants.append( r.Resolve() )
       
   976 				except BadReferenceError:
       
   977 					self.owner.Error("Missing variant '%s'", r.ref)
       
   978 
       
   979 		name = self.name
       
   980 
       
   981 		for v in self.modifiers:
       
   982 			name = name + "." + v.name
       
   983 
       
   984 		return [ BuildUnit(name, self.variants + self.modifiers) ]
       
   985 
       
   986 
       
   987 class AliasRef(Reference):
       
   988 
       
   989 	def __init__(self, ref=None):
       
   990 		Reference.__init__(self, ref)
       
   991 
       
   992 	def DebugPrint(self, prefix = ""):
       
   993 		self.owner.Debug("%s<aliasRef ref='%s'/>", prefix, self.ref)
       
   994 
       
   995 	def Resolve(self):
       
   996 		try:
       
   997 			return self.owner.cache.FindNamedAlias(self.ref)
       
   998 		except KeyError:
       
   999 			raise BadReferenceError(self.ref)
       
  1000 
       
  1001 
       
  1002 class Group(Model, Config):
       
  1003 	def __init__(self, name=""):
       
  1004 		Model.__init__(self)
       
  1005 		Config.__init__(self)
       
  1006 		self.name = name
       
  1007 		self.childRefs = []
       
  1008 
       
  1009 	def SetProperty(self, key, val):
       
  1010 		if key == "name":
       
  1011 			self.name = val
       
  1012 		else:
       
  1013 			raise InvalidPropertyError()
       
  1014 
       
  1015 	def AddChild(self, child):
       
  1016 		if isinstance( child, (VariantRef,AliasRef,GroupRef) ):
       
  1017 			self.childRefs.append(child)
       
  1018 		else:
       
  1019 			raise InvalidChildError()
       
  1020 
       
  1021 	def SetOwner(self, raptor):
       
  1022 		Model.SetOwner(self, raptor)
       
  1023 		for r in self.childRefs:
       
  1024 			r.SetOwner(raptor)
       
  1025 
       
  1026 	def Valid(self):
       
  1027 		return self.name and self.childRefs
       
  1028 
       
  1029 	def DebugPrint(self, prefix = ""):
       
  1030 
       
  1031 		self.owner.Debug("<group name='%s'>", prefix, self.name)
       
  1032 
       
  1033 		for r in self.childRefs:
       
  1034 			r.DebugPrint(prefix + self.indent)
       
  1035 
       
  1036 		self.owner.Debug("%s</group>", prefix)
       
  1037 
       
  1038 	def GenerateBuildUnits(self):
       
  1039 
       
  1040 		units = []
       
  1041 
       
  1042 		for r in self.childRefs:
       
  1043 			refMods = r.GetModifiers()
       
  1044 
       
  1045 			try:
       
  1046 				obj = r.Resolve()
       
  1047 			except BadReferenceError:
       
  1048 				self.owner.Error("Missing variant '%s'", r.ref)
       
  1049 			else:
       
  1050 				obj.ClearModifiers()
       
  1051 
       
  1052 				for m in refMods + self.modifiers:
       
  1053 					obj.AddModifier(m)
       
  1054 
       
  1055 				units.extend( obj.GenerateBuildUnits() )
       
  1056 
       
  1057 		return units
       
  1058 
       
  1059 
       
  1060 class GroupRef(Reference):
       
  1061 
       
  1062 	def __init__(self, ref=None):
       
  1063 		Reference.__init__(self, ref)
       
  1064 
       
  1065 	def DebugPrint(self, prefix = ""):
       
  1066 		mod = ".".join(self.modifiers)
       
  1067 		self.owner.Debug("%s<groupRef ref='%s' mod='%s'/>", prefix, self.ref, mod)
       
  1068 
       
  1069 	def Resolve(self):
       
  1070 		try:
       
  1071 			return self.owner.cache.FindNamedGroup(self.ref)
       
  1072 		except KeyError:
       
  1073 			raise BadReferenceError(self.ref)
       
  1074 
       
  1075 class Tool(object):
       
  1076 	"""Represents a tool that might be used by raptor e.g. a compiler"""
       
  1077 
       
  1078 	# For use in dealing with tools that return non-ascii version strings.
       
  1079 	nonascii = ""
       
  1080 	identity_chartable = chr(0)
       
  1081 	for c in xrange(1,128):
       
  1082 		identity_chartable += chr(c)
       
  1083 	for c in xrange(128,256):
       
  1084 		nonascii += chr(c)
       
  1085 		identity_chartable += " "
       
  1086 
       
  1087 	def __init__(self, name, command, versioncommand, versionresult, id="", log = raptor_utilities.nulllog):
       
  1088 		self.name = name
       
  1089 		self.command = command
       
  1090 		self.versioncommand = versioncommand
       
  1091 		self.versionresult = versionresult
       
  1092 		self.id = id # what config this is from - used in debug messages
       
  1093 		self.date = None
       
  1094 
       
  1095 
       
  1096 		# Assume the tool is unavailable or the wrong
       
  1097 		# version until someone proves that it's OK
       
  1098 		self.valid = False
       
  1099 
       
  1100 		self.log=log
       
  1101 
       
  1102 	def expand(self, toolset):
       
  1103 		self.versioncommand = toolset.ExpandAll(self.versioncommand)
       
  1104 		self.versionresult  = toolset.ExpandAll(self.versionresult)
       
  1105 		self.command = toolset.ExpandAll(self.command)
       
  1106 		self.key = hashlib.md5(self.versioncommand + self.versionresult).hexdigest()
       
  1107 		
       
  1108 		# We need the tool's date to find out if we should check it.
       
  1109 		try:
       
  1110 			if '/' in self.command:
       
  1111 				testfile = os.path.abspath(self.command.strip("\"'"))
       
  1112 			else:
       
  1113 				# The tool isn't a relative or absolute path so the could be relying on the 
       
  1114 				# $PATH variable to make it available.  We must find the tool if it's a simple 
       
  1115 				# executable file (e.g. "armcc" rather than "python myscript.py") then get it's date. 
       
  1116 				# We can use the date later to see if our cache is valid. 
       
  1117 				# If it really is not a simple command then we won't be able to get a date and
       
  1118 				# we won't be able to tell if it is altered or updated - too bad!
       
  1119 				testfile = generic_path.Where(self.command)
       
  1120 				self.log.Debug("toolcheck: tool '%s' was found on the path at '%s' ", self.command, testfile)
       
  1121 				if testfile is None:
       
  1122 					raise Exception("Can't be found in path")
       
  1123 
       
  1124 			if not os.path.isfile(testfile):
       
  1125 				raise Exception("tool %s appears to not be a file %s", self.command, testfile)
       
  1126 				
       
  1127 			testfile_stat = os.stat(testfile)
       
  1128 			self.date = testfile_stat.st_mtime
       
  1129 		except Exception,e:
       
  1130 			self.log.Debug("toolcheck: '%s=%s' cannot be dated - this is ok, but the toolcheck won't be able to tell when a new  of the tool is installed. (%s)", self.name, self.command, str(e))
       
  1131 	
       
  1132 			
       
  1133 	def check(self, shell, evaluator):
       
  1134 
       
  1135 		self.vre = re.compile(self.versionresult)
       
  1136 
       
  1137 		try:
       
  1138 			self.log.Debug("Pre toolcheck: '%s' for version '%s'", self.name, self.versionresult)
       
  1139 			p = subprocess.Popen(args=[shell, "-c", self.versioncommand], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
       
  1140 			versionoutput,err = p.communicate()
       
  1141 			self.log.Debug("Checking tool '%s' for version '%s'", self.name, self.versionresult)
       
  1142 		except Exception,e:
       
  1143 			versionoutput=None
       
  1144 
       
  1145 		# Some tools return version strings with unicode characters! 
       
  1146 		# There is no good response other than a lot of decoding and encoding.
       
  1147 		# Simpler to ignore it:
       
  1148 		versionoutput_a = versionoutput.translate(Tool.identity_chartable,"")
       
  1149 
       
  1150 		if versionoutput_a and self.vre.search(versionoutput_a) != None:
       
  1151 			self.log.Debug("tool '%s' returned an acceptable version '%s' at %s", self.name, versionoutput_a, str(self.date))
       
  1152 			self.valid = True
       
  1153 		else:
       
  1154 			self.log.Error("tool '%s' from config '%s' did not return version '%s' as required.\nCommand '%s' returned:\n%s\nCheck your environment and configuration.\n", self.name, self.id, self.versionresult, self.versioncommand, versionoutput_a)
       
  1155 			self.valid = False
       
  1156 		return self.valid
       
  1157 
       
  1158 def envhash(irrelevant_vars):
       
  1159 	"""Determine something unique about this environment to identify it.
       
  1160 	must ignore variables that change without mattering to the caller
       
  1161 	e.g. perhaps PATH matters but PWD and PPID don't"""
       
  1162 	envid = hashlib.md5()
       
  1163 	for k in os.environ:
       
  1164 		if k not in irrelevant_vars:
       
  1165 			envid.update(os.environ[k])
       
  1166 	return envid.hexdigest()[:16]
       
  1167 
       
  1168 
       
  1169 class ToolSet(object):
       
  1170 	""" 
       
  1171 	This class manages a bunch of tools and keeps a cache of
       
  1172 	all tools that it ever sees (across all configurations).
       
  1173 	toolset.check() is called for each config but the cache is kept across calls to
       
  1174 	catch the use of one tool in many configs.
       
  1175 	write() is used to flush the cache to disc.
       
  1176 	"""
       
  1177 	# The raptor shell - this is not mutable.
       
  1178 	hostbinaries = os.path.join(os.environ['SBS_HOME'], 
       
  1179 	                            os.environ['HOSTPLATFORM_DIR'])
       
  1180 	                            
       
  1181 	if HostPlatform.IsHost('lin*'):
       
  1182 		shell=os.path.join(hostbinaries, 'bin/bash')
       
  1183 	else:
       
  1184 		if 'SBS_CYGWIN' in os.environ:
       
  1185 			shell=os.path.join(os.environ['SBS_CYGWIN'], 'bin\\bash.exe')
       
  1186 		else:
       
  1187 			shell=os.path.join(hostbinaries, 'cygwin\\bin\\bash.exe')
       
  1188 
       
  1189 
       
  1190 	irrelevant_vars = ['PWD','OLDPWD','PID','PPID', 'SHLVL' ]
       
  1191 
       
  1192 
       
  1193 	shell_version=".*GNU bash, version [34].*"
       
  1194 	shell_re = re.compile(shell_version)
       
  1195 	if 'SBS_BUILD_DIR' in os.environ:
       
  1196 		cachefile_basename = str(generic_path.Join(os.environ['SBS_BUILD_DIR'],"toolcheck_cache_"))
       
  1197 	elif 'EPOCROOT' in os.environ:
       
  1198 		cachefile_basename = str(generic_path.Join(os.environ['EPOCROOT'],"epoc32/build/toolcheck_cache_"))
       
  1199 	else:
       
  1200 		cachefile_basename = None
       
  1201 
       
  1202 	tool_env_id = envhash(irrelevant_vars)
       
  1203 	filemarker = "sbs_toolcache_2.8.2"
       
  1204 
       
  1205 	def __init__(self, log = raptor_utilities.nulllog, forced=False):
       
  1206 		self.__toolcheckcache = {}
       
  1207 
       
  1208 		self.valid = True
       
  1209 		self.checked = False
       
  1210 		self.shellok = False
       
  1211 		self.configname=""
       
  1212 		self.cache_loaded = False
       
  1213 		self.forced = forced
       
  1214 
       
  1215 		self.log=log
       
  1216 
       
  1217 		# Read in the tool cache
       
  1218 		#
       
  1219 		# The cache format is a hash key which identifies the
       
  1220 		# command and the version that we're checking for. Then
       
  1221 		# there are name,value pairs that record, e.g. the date
       
  1222 		# of the command file or the name of the variable that
       
  1223 		# the config uses for the tool (GNUCP or MWCC or whatever)
       
  1224 
       
  1225 		if ToolSet.cachefile_basename:
       
  1226 			self.cachefilename = ToolSet.cachefile_basename+".tmp"
       
  1227 			if not self.forced:
       
  1228 				try:
       
  1229 					f = open(self.cachefilename, "r+")
       
  1230 					# if this tool cache was recorded in
       
  1231 					# a different environment then ignore it.
       
  1232 					marker = f.readline().rstrip("\r\n")
       
  1233 					if marker == ToolSet.filemarker:
       
  1234 						env_id_tmp = f.readline().rstrip("\r\n")
       
  1235 						if env_id_tmp == ToolSet.tool_env_id:
       
  1236 							try:
       
  1237 								for l in f.readlines():
       
  1238 									toolhistory  = l.rstrip(",\n\r").split(",")
       
  1239 									ce = {}
       
  1240 									for i in toolhistory[1:]:
       
  1241 										(name,val) = i.split("=")
       
  1242 										if name == "valid":
       
  1243 											val = bool(val)
       
  1244 										elif name == "age":
       
  1245 											val = int(val)
       
  1246 										elif name == "date":
       
  1247 											if val != "None":
       
  1248 												val = float(val)
       
  1249 											else:
       
  1250 												val= None
       
  1251 
       
  1252 										ce[name] = val
       
  1253 									self.__toolcheckcache[toolhistory[0]] = ce
       
  1254 								log.Info("Loaded toolcheck cache: %s\n", self.cachefilename)
       
  1255 							except Exception, e:
       
  1256 								log.Info("Ignoring garbled toolcheck cache: %s (%s)\n", self.cachefilename, str(e))
       
  1257 								self.__toolcheckcache = {}
       
  1258 								
       
  1259 									
       
  1260 						else:
       
  1261 							log.Info("Toolcheck cache %s ignored - environment changed\n", self.cachefilename)
       
  1262 					else:
       
  1263 						log.Info("Toolcheck cache not loaded = marker missing: %s %s\n", self.cachefilename, ToolSet.filemarker)
       
  1264 					f.close()
       
  1265 				except IOError, e:
       
  1266 					log.Info("Failed to load toolcheck cache: %s\n", self.cachefilename)
       
  1267 		else:
       
  1268 			log.Debug("Toolcheck cachefile not created because EPOCROOT not set in environment.\n")
       
  1269 
       
  1270 	def check_shell(self):
       
  1271 		# The command shell is a critical tool because all the other tools run
       
  1272 		# within it so we must check for it first. It has to be in the path.
       
  1273 		# bash 4 is preferred, 3 is accepted
       
  1274 		try:
       
  1275 			p = subprocess.Popen(args=[ToolSet.shell, '--version'], bufsize=1024, shell = False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
       
  1276 			shellversion_out, errtxt = p.communicate()
       
  1277 			if ToolSet.shell_re.search(shellversion_out) == None:
       
  1278 				self.log.Error("A critical tool, '%s', did not return the required version '%s':\n%s\nPlease check that '%s' is in the path.", ToolSet.shell, ToolSet.shell_version, shellversion_out, ToolSet.shell)
       
  1279 				self.valid = False
       
  1280 		except Exception,e:
       
  1281 			self.log.Error("A critical tool could not be found.\nPlease check that '%s' is in the path. (%s)", ToolSet.shell,  str(e))
       
  1282 			self.valid = False
       
  1283 
       
  1284 		return self.valid
       
  1285 
       
  1286 	def check(self, evaluator, configname):
       
  1287 		"""Check the toolset for a particular config"""
       
  1288 
       
  1289 		self.checked = True # remember that we did check something
       
  1290 
       
  1291 		if not self.shellok:
       
  1292 			self.shellok = self.check_shell()
       
  1293 		self.shellok = True
       
  1294 
       
  1295 		self.valid = self.valid and self.shellok
       
  1296 
       
  1297 		cache = self.__toolcheckcache 
       
  1298 		for tool in evaluator.tools:
       
  1299 			if not self.forced:
       
  1300 				try:
       
  1301 					t = cache[tool.key]
       
  1302 						
       
  1303 				except KeyError,e:
       
  1304 					pass
       
  1305 				else:
       
  1306 					# if the cache has an entry for the tool then see if the date on
       
  1307 					# the tool has changed (assuming the tool is a simple executable file)
       
  1308 					if t.has_key('date') and (tool.date is None or (tool.date - t['date'] > 0.1))  :
       
  1309 						self.log.Debug("toolcheck forced: '%s'  changed since the last check: %s < %s", tool.command, str(t['date']), str(tool.date))
       
  1310 					else:
       
  1311 						t['age'] = 0 # we used it so it's obviously needed
       
  1312 						self.valid = self.valid and t['valid']
       
  1313 						self.log.Debug("toolcheck saved on: '%s'", tool.name)
       
  1314 						continue
       
  1315 
       
  1316 
       
  1317 			self.log.Debug("toolcheck done: %s -key: %s" % (tool.name, tool.key))
       
  1318 
       
  1319 			if not tool.check(ToolSet.shell, evaluator):
       
  1320 				self.valid = False
       
  1321 
       
  1322 			# Tool failures are cached just like successes - don't want to repeat them
       
  1323 			cache[tool.key] =  { "name" : tool.name, "valid" : tool.valid, "age" : 0 , "date" : tool.date }
       
  1324 
       
  1325 
       
  1326 	def write(self):
       
  1327 		"""Writes the tool check cache to disc.
       
  1328 
       
  1329 		   toolset.write()
       
  1330 		"""
       
  1331 		cache = self.__toolcheckcache 
       
  1332 
       
  1333 		# Write out the cache.
       
  1334 		if self.checked and ToolSet.cachefile_basename:
       
  1335 			self.log.Debug("Saving toolcache: %s", self.cachefilename)
       
  1336 			try:
       
  1337 				f = open(self.cachefilename, "w+")
       
  1338 				f.write(ToolSet.filemarker+"\n")
       
  1339 				f.write(ToolSet.tool_env_id+"\n")
       
  1340 				for k,ce in cache.iteritems():
       
  1341 
       
  1342 					# If a tool has not been used for an extraordinarily long time
       
  1343 					# then forget it - to prevent the cache from clogging up with old tools.
       
  1344 					# Only write entries for tools that were found to be ok - so that the 
       
  1345 					# next time the ones that weren't will be re-tested
       
  1346 
       
  1347 					if ce['valid'] and ce['age'] < 100:
       
  1348 						ce['age'] += 1
       
  1349 						f.write("%s," % k)
       
  1350 						for n,v in ce.iteritems():
       
  1351 							f.write("%s=%s," % (n,str(v)))
       
  1352 					f.write("\n")
       
  1353 				f.close()
       
  1354 				self.log.Info("Created/Updated toolcheck cache: %s\n", self.cachefilename)
       
  1355 			except Exception, e:
       
  1356 				self.log.Info("Could not write toolcheck cache: %s", str(e))
       
  1357 		return self.valid
       
  1358 
       
  1359 
       
  1360 class Evaluator(object):
       
  1361 	"""Determine the values of variables under different Configurations.
       
  1362 	Either of specification and buildUnit may be None."""
       
  1363 
       
  1364 
       
  1365 	refRegex = re.compile("\$\((.+?)\)")
       
  1366 
       
  1367 	def __init__(self, Raptor, specification, buildUnit, gathertools = False):
       
  1368 		self.raptor = Raptor
       
  1369 		self.dict = {}
       
  1370 		self.tools = []
       
  1371 		self.gathertools = gathertools
       
  1372 
       
  1373 		specName = "none"
       
  1374 		configName = "none"
       
  1375 
       
  1376 		# A list of lists of operations.
       
  1377 		opsLists = []
       
  1378 
       
  1379 		if buildUnit:
       
  1380 			opsLists.extend( buildUnit.GetOperations() )
       
  1381 
       
  1382 		if specification:
       
  1383 			for v in specification.GetAllVariantsRecursively():
       
  1384 				opsLists.extend( v.GetAllOperationsRecursively() )
       
  1385 
       
  1386 		tools = {}
       
  1387 
       
  1388 		for opsList in opsLists:
       
  1389 			for op in opsList:
       
  1390 				# applying an Operation to a non-existent variable
       
  1391 				# is OK. We assume that it is just an empty string.
       
  1392 				try:
       
  1393 					oldValue = self.dict[op.name]
       
  1394 				except KeyError:
       
  1395 					oldValue = ""
       
  1396 
       
  1397 				newValue = op.Apply(oldValue)
       
  1398 				self.dict[op.name] = newValue
       
  1399 			
       
  1400 				if self.gathertools:
       
  1401 					if op.type == "tool" and op.versionCommand and op.versionResult:
       
  1402 						tools[op.name] = Tool(op.name, newValue, op.versionCommand, op.versionResult, configName, log = self.raptor)
       
  1403 
       
  1404 
       
  1405 		if self.gathertools:
       
  1406 			self.tools = tools.values()
       
  1407 		else:
       
  1408 			self.tools=[]
       
  1409 
       
  1410 		# resolve inter-variable references in the dictionary
       
  1411 		unresolved = True
       
  1412 
       
  1413 		for k, v in self.dict.items():
       
  1414 			self.dict[k] = v.replace("$$","__RAPTOR_ESCAPED_DOLLAR__")
       
  1415 
       
  1416 		while unresolved:
       
  1417 			unresolved = False
       
  1418 			for k, v in self.dict.items():
       
  1419 				if v.find('$(' + k + ')') != -1:
       
  1420 					self.raptor.Error("Recursion Detected in variable '%s' in configuration '%s' ",k,configName)
       
  1421 					expanded = "RECURSIVE_INVALID_STRING"
       
  1422 				else:
       
  1423 					expanded = self.ExpandAll(v, specName, configName)
       
  1424 
       
  1425 				if expanded != v:				# something changed?
       
  1426 					self.dict[k] = expanded
       
  1427 					unresolved = True			# maybe more to do
       
  1428 
       
  1429 		# unquote double-dollar references
       
  1430 		for k, v in self.dict.items():
       
  1431 			self.dict[k] = v.replace("__RAPTOR_ESCAPED_DOLLAR__","$")
       
  1432 
       
  1433 		for t in self.tools:
       
  1434 			t.expand(self)
       
  1435 
       
  1436 
       
  1437 
       
  1438 	def Get(self, name):
       
  1439 		"""return the value of variable 'name' or None if not found."""
       
  1440 
       
  1441 		if name in self.dict:
       
  1442 			return self.dict[name]
       
  1443 		else:
       
  1444 			return None
       
  1445 
       
  1446 
       
  1447 	def Resolve(self, name):
       
  1448 		"""same as Get except that env variables are expanded.
       
  1449 
       
  1450 		raises BadReferenceError if the variable 'name' exists but a
       
  1451 		contained environment variable does not exist."""
       
  1452 		return self.Get(name) # all variables are now expanded anyway
       
  1453 
       
  1454 
       
  1455 	def ResolveMatching(self, pattern):
       
  1456 		""" Return a dictionary of all variables that match the pattern """
       
  1457 		for k,v in self.dict.iteritems():
       
  1458 			if pattern.match(k):
       
  1459 				yield (k,v)
       
  1460 
       
  1461 
       
  1462 	def ExpandAll(self, value, spec = "none", config = "none"):
       
  1463 		"""replace all $(SOMETHING) in the string value.
       
  1464 
       
  1465 		returns the newly expanded string."""
       
  1466 
       
  1467 		refs = Evaluator.refRegex.findall(value)
       
  1468 
       
  1469 		for r in set(refs):
       
  1470 			expansion = None
       
  1471 
       
  1472 			if r in self.raptor.override:
       
  1473 				expansion = self.raptor.override[r]
       
  1474 			elif r in self.dict:
       
  1475 				expansion = self.dict[r]
       
  1476 			else:
       
  1477 				# no expansion for $(r)
       
  1478 				self.raptor.Error("Unset variable '%s' used in spec '%s' with config '%s'",
       
  1479 							  	  r, spec, config)
       
  1480 			if expansion != None:
       
  1481 				value = value.replace("$(" + r + ")", expansion)
       
  1482 
       
  1483 		return value
       
  1484 
       
  1485 
       
  1486 # raptor_data module functions
       
  1487 
       
  1488 
       
  1489 # end of the raptor_data module