sbsv2/raptor/python/raptor_meta.py
changeset 0 044383f39525
child 2 86356a777616
child 3 e1eecf4d390d
child 590 360bd6b35136
equal deleted inserted replaced
-1:000000000000 0:044383f39525
       
     1 #
       
     2 # Copyright (c) 2007-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 # This module includes classes that process bld.inf and .mmp files to
       
    16 # generate Raptor build specifications
       
    17 #
       
    18 
       
    19 import copy
       
    20 import re
       
    21 import os.path
       
    22 import shutil
       
    23 import stat
       
    24 import hashlib
       
    25 import base64
       
    26 
       
    27 import raptor
       
    28 import raptor_data
       
    29 import raptor_utilities
       
    30 import raptor_xml
       
    31 import generic_path
       
    32 import subprocess
       
    33 import zipfile
       
    34 from mmpparser import *
       
    35 
       
    36 import time
       
    37 
       
    38 
       
    39 PiggyBackedBuildPlatforms = {'ARMV5':['GCCXML']}
       
    40 
       
    41 PlatformDefaultDefFileDir = {'WINSCW':'bwins',
       
    42 				  'ARMV5' :'eabi',
       
    43 				  'ARMV5SMP' :'eabi',
       
    44 				  'GCCXML':'eabi',
       
    45 				  'ARMV6':'eabi',
       
    46 				  'ARMV7' : 'eabi',
       
    47 				  'ARMV7SMP' : 'eabi'}
       
    48 
       
    49 def getVariantCfgDetail(aEPOCROOT, aVariantCfgFile):
       
    50 	"""Obtain pertinent build related detail from the Symbian variant.cfg file.
       
    51 
       
    52 	This variant.cfg file, usually located relative to $(EPOCROOT), contains:
       
    53 	(1) The $(EPOCROOT) relative location of the primary .hrh file used to configure the specific OS variant build
       
    54 	(2) A flag determining whether ARMV5 represents an ABIV1 or ABIV2 build (currently unused by Raptor)."""
       
    55 
       
    56 	variantCfgDetails = {}
       
    57 	variantCfgFile = None
       
    58 
       
    59 	try:
       
    60 		variantCfgFile = open(str(aVariantCfgFile))
       
    61 	except IOError, (number, message):
       
    62 		raise MetaDataError("Could not read variant configuration file "+str(aVariantCfgFile)+" ("+message+")")
       
    63 
       
    64 	for line in variantCfgFile.readlines():
       
    65 		if re.search('^(\s$|\s*#)', line):
       
    66 			continue
       
    67 		# Note that this detection of the .hrh file matches the command line build i.e. ".hrh" somewhere
       
    68 		# in the specified line
       
    69 		elif re.search('\.hrh', line, re.I):
       
    70 			variantHrh = line.strip()
       
    71 			if variantHrh.startswith('\\') or variantHrh.startswith('/'):
       
    72 				variantHrh = variantHrh[1:]
       
    73 			variantHrh = aEPOCROOT.Append(variantHrh)
       
    74 			variantCfgDetails['VARIANT_HRH'] = variantHrh
       
    75 		else:
       
    76 			lineContent = line.split()
       
    77 
       
    78 			if len(lineContent) == 1:
       
    79 				variantCfgDetails[lineContent.pop(0)] = 1
       
    80 			else:
       
    81 				variantCfgDetails[lineContent.pop(0)] = lineContent
       
    82 
       
    83 	variantCfgFile.close()
       
    84 
       
    85 	if not variantCfgDetails.has_key('VARIANT_HRH'):
       
    86 		raise MetaDataError("No variant file specified in "+str(aVariantCfgFile))
       
    87 	if not variantHrh.isFile():
       
    88 		raise MetaDataError("Variant file "+str(variantHrh)+" does not exist")
       
    89 
       
    90 	return variantCfgDetails
       
    91 
       
    92 def getOsVerFromKifXml(aPathToKifXml):
       
    93 	"""Obtain the OS version from the kif.xml file located at $EPOCROOT/epoc32/data/kif.xml.
       
    94 
       
    95 	If successful, the function returns a string such as "v95" to indicate 9.5; None is
       
    96 	returned if for any reason the function cannot determine the OS version."""
       
    97 
       
    98 	releaseTagName = "ki:release"
       
    99 	osVersion = None
       
   100 
       
   101 	import xml.dom.minidom
       
   102 
       
   103 	try:
       
   104 		# Parsed document object
       
   105 		kifDom = xml.dom.minidom.parse(str(aPathToKifXml))
       
   106 
       
   107 		# elements - the elements whose names are releaseTagName
       
   108 		elements = kifDom.getElementsByTagName(releaseTagName)
       
   109 
       
   110 		# There should be exactly one of the elements whose name is releaseTagName
       
   111 		# If more than one, osVersion is left as None, since the version should be
       
   112 		# unique to the kif.xml file
       
   113 		if len(elements) == 1:
       
   114 			osVersionTemp = elements[0].getAttribute("version")
       
   115 			osVersion = "v" + osVersionTemp.replace(".", "")
       
   116 
       
   117 		kifDom.unlink() # Clean up
       
   118 
       
   119 	except:
       
   120 		# There's no documentation on which exceptions are raised by these functions.
       
   121 		# We catch everything and assume any exception means there was a failure to
       
   122 		# determine OS version. None is returned, and the code will fall back
       
   123 		# to looking at the buildinfo.txt file.
       
   124 		pass
       
   125 
       
   126 	return osVersion
       
   127 
       
   128 def getOsVerFromBuildInfoTxt(aPathToBuildInfoTxt):
       
   129 	"""Obtain the OS version from the buildinfo.txt file located at $EPOCROOT/epoc32/data/buildinfo.txt.
       
   130 
       
   131 	If successful, the function returns a string such as "v95" to indicate 9.5; None is
       
   132 	returned if for any reason the function cannot determine the OS version.
       
   133 
       
   134 	The file $EPOCROOT/epoc32/data/buildinfo.txt is presumed to exist. The client code should
       
   135 	handle existance/non-existance."""
       
   136 
       
   137 	pathToBuildInfoTxt = str(aPathToBuildInfoTxt) # String form version of path to buildinfo.txt
       
   138 
       
   139 	# Open the file for reading; throw an exception if it could not be read - note that
       
   140 	# it should exist at this point.
       
   141 	try:
       
   142 		buildInfoTxt = open(pathToBuildInfoTxt)
       
   143 	except IOError, (number, message):
       
   144 		raise MetaDataError("Could not read buildinfo.txt file at" + pathToBuildInfoTxt + ": (" + message + ")")
       
   145 
       
   146 	# Example buildinfo.txt contents:
       
   147 	#
       
   148 	# DeviceFamily               100
       
   149 	# DeviceFamilyRev            0x900
       
   150 	# ManufacturerSoftwareBuild  M08765_Symbian_OS_v9.5
       
   151 	#
       
   152 	# Regexp to match the line containing the OS version
       
   153 	# Need to match things like M08765_Symbian_OS_v9.5 and M08765_Symbian_OS_vFuture
       
   154 	# So for the version, match everything except whitespace after v. Whitespace
       
   155 	# signifies the end of the regexp.
       
   156 	osVersionMatcher = re.compile('.*_Symbian_OS_v([^\s]*)', re.I)
       
   157 	osVersion = None
       
   158 
       
   159 	# Search for a regexp match over all the times in the file
       
   160 	# Note: if two or more lines match the search pattern then
       
   161 	# the latest match will overwrite the osVersion string.
       
   162 	for line in buildInfoTxt:
       
   163 		matchResult = osVersionMatcher.match(line)
       
   164 		if matchResult:
       
   165 			result = matchResult.groups()
       
   166 			osVersion = "v" +  str(reduce(lambda x, y: x + y, result))
       
   167 			osVersion = osVersion.replace(".", "")
       
   168 
       
   169 	buildInfoTxt.close() # Clean-up
       
   170 
       
   171 	return osVersion
       
   172 
       
   173 def getBuildableBldInfBuildPlatforms(aBldInfBuildPlatforms,
       
   174 									aDefaultOSBuildPlatforms,
       
   175 									aBaseDefaultOSBuildPlatforms,
       
   176 									aBaseUserDefaultOSBuildPlatforms):
       
   177 	"""Obtain a set of build platform names supported by a bld.inf file
       
   178 
       
   179 	Build platform deduction is based on both the contents of the PRJ_PLATFORMS section of
       
   180 	a bld.inf file together with a hard-coded set of default build platforms supported by
       
   181 	the build system itself."""
       
   182 
       
   183 	expandedBldInfBuildPlatforms = []
       
   184 	removePlatforms = set()
       
   185 
       
   186 	for bldInfBuildPlatform in aBldInfBuildPlatforms:
       
   187 		if bldInfBuildPlatform.upper() == "DEFAULT":
       
   188 			expandedBldInfBuildPlatforms.extend(aDefaultOSBuildPlatforms.split())
       
   189 		elif bldInfBuildPlatform.upper() == "BASEDEFAULT":
       
   190 			expandedBldInfBuildPlatforms.extend(aBaseDefaultOSBuildPlatforms.split())
       
   191 		elif bldInfBuildPlatform.upper() == "BASEUSERDEFAULT":
       
   192 			expandedBldInfBuildPlatforms.extend(aBaseUserDefaultOSBuildPlatforms.split())
       
   193 		elif bldInfBuildPlatform.startswith("-"):
       
   194 			removePlatforms.add(bldInfBuildPlatform.lstrip("-").upper())
       
   195 		else:
       
   196 			expandedBldInfBuildPlatforms.append(bldInfBuildPlatform.upper())
       
   197 
       
   198 	if len(expandedBldInfBuildPlatforms) == 0:
       
   199 		expandedBldInfBuildPlatforms.extend(aDefaultOSBuildPlatforms.split())
       
   200 
       
   201 	# make a set of platforms that can be built
       
   202 	buildableBldInfBuildPlatforms = set(expandedBldInfBuildPlatforms)
       
   203 
       
   204 	# Add platforms that are buildable by virtue of the presence of another
       
   205 	for piggyBackedPlatform in PiggyBackedBuildPlatforms:
       
   206 		if piggyBackedPlatform in buildableBldInfBuildPlatforms:
       
   207 			buildableBldInfBuildPlatforms.update(PiggyBackedBuildPlatforms.get(piggyBackedPlatform))
       
   208 
       
   209 	# Remove platforms that were negated
       
   210 	buildableBldInfBuildPlatforms -= removePlatforms
       
   211 
       
   212 	return buildableBldInfBuildPlatforms
       
   213 
       
   214 
       
   215 def getPreProcessorCommentDetail (aPreProcessorComment):
       
   216 	"""Takes a preprocessor comment and returns an array containing the filename and linenumber detail."""
       
   217 
       
   218 	commentDetail = []
       
   219 	commentMatch = re.search('# (?P<LINENUMBER>\d+) "(?P<FILENAME>.*)"', aPreProcessorComment)
       
   220 
       
   221 	if commentMatch:
       
   222 		filename = commentMatch.group('FILENAME')
       
   223 		filename = os.path.abspath(filename)
       
   224 		filename = re.sub(r'\\\\', r'\\', filename)
       
   225 		filename = re.sub(r'//', r'/', filename)
       
   226 		filename = generic_path.Path(filename)
       
   227 		linenumber = int (commentMatch.group('LINENUMBER'))
       
   228 
       
   229 		commentDetail.append(filename)
       
   230 		commentDetail.append(linenumber)
       
   231 
       
   232 	return commentDetail
       
   233 
       
   234 
       
   235 # Classes
       
   236 
       
   237 class MetaDataError(Exception):
       
   238 	"""Fatal error wrapper, to be thrown directly back to whatever is calling."""
       
   239 
       
   240 	def __init__(self, aText):
       
   241 		self.Text = aText
       
   242 	def __str__(self):
       
   243 		return repr(self.Text)
       
   244 
       
   245 
       
   246 class PreProcessedLine(str):
       
   247 	"""Custom string class that accepts filename and line number information from
       
   248 	a preprocessed context."""
       
   249 
       
   250 	def __new__(cls, value, *args, **keywargs):
       
   251 		return str.__new__(cls, value)
       
   252 
       
   253 	def __init__(self, value, aFilename, aLineNumber):
       
   254 		self.filename = aFilename
       
   255 		self.lineNumber = aLineNumber
       
   256 
       
   257 	def getFilename (self):
       
   258 		return self.filename
       
   259 
       
   260 	def getLineNumber (self):
       
   261 		return self.lineNumber
       
   262 
       
   263 class PreProcessor(raptor_utilities.ExternalTool):
       
   264 	"""Preprocessor wrapper suitable for Symbian metadata file processing."""
       
   265 
       
   266 	def __init__(self, aPreProcessor,
       
   267 				 aStaticOptions,
       
   268 				 aIncludeOption,
       
   269 				 aMacroOption,
       
   270 				 aPreIncludeOption,
       
   271 				 aRaptor):
       
   272 		raptor_utilities.ExternalTool.__init__(self, aPreProcessor)
       
   273 		self.__StaticOptions = aStaticOptions
       
   274 		self.__IncludeOption = aIncludeOption
       
   275 		self.__MacroOption = aMacroOption
       
   276 		self.__PreIncludeOption = aPreIncludeOption
       
   277 
       
   278 		self.filename = ""
       
   279 		self.__Macros = []
       
   280 		self.__IncludePaths = []
       
   281 		self.__PreIncludeFile = ""
       
   282 		self.raptor = aRaptor
       
   283 
       
   284 	def call(self, aArgs, sourcefilename):
       
   285 		""" Override call so that we can do our own error handling."""
       
   286 		tool = self._ExternalTool__Tool
       
   287 		try:
       
   288 			commandline = tool + " " + aArgs + " " + str(sourcefilename)
       
   289 
       
   290 			# the actual call differs between Windows and Unix
       
   291 			if raptor_utilities.getOSFileSystem() == "unix":
       
   292 				p = subprocess.Popen(commandline, \
       
   293 									 shell=True, bufsize=65535, \
       
   294 									 stdin=subprocess.PIPE, \
       
   295 									 stdout=subprocess.PIPE, \
       
   296 									 stderr=subprocess.PIPE, \
       
   297 									 close_fds=True)
       
   298 			else:
       
   299 				p = subprocess.Popen(commandline, \
       
   300 									 bufsize=65535, \
       
   301 									 stdin=subprocess.PIPE, \
       
   302 									 stdout=subprocess.PIPE, \
       
   303 									 stderr=subprocess.PIPE, \
       
   304 									 universal_newlines=True)
       
   305 
       
   306 			# run the command and wait for all the output
       
   307 			(self._ExternalTool__Output, errors) = p.communicate()
       
   308 
       
   309 			if self.raptor.debugOutput:
       
   310 				self.raptor.Debug("Preprocessing Start %s", str(sourcefilename))
       
   311 				self.raptor.Debug("Output:\n%s", self._ExternalTool__Output)
       
   312 				self.raptor.Debug("Errors:\n%s", errors)
       
   313 				self.raptor.Debug("Preprocessing End %s", str(sourcefilename))
       
   314 
       
   315 			incRE = re.compile("In file included from")
       
   316 			fromRE = re.compile(r"\s+from")
       
   317 			warningRE = re.compile("warning:|pasting.+token|from.+:")
       
   318 			remarkRE = re.compile("no newline at end of file|does not give a valid preprocessing token")
       
   319 
       
   320 			actualErr = False
       
   321 			if errors != "":
       
   322 				for error in errors.splitlines():
       
   323 					if incRE.search(error) or fromRE.search(error):
       
   324 						continue
       
   325 					if not remarkRE.search(error):
       
   326 						if warningRE.search(error):
       
   327 							self.raptor.Warn("%s: %s", tool, error)
       
   328 						else:
       
   329 							self.raptor.Error("%s: %s", tool, error)
       
   330 							actualErr = True
       
   331 			if actualErr:
       
   332 				raise MetaDataError("Errors in %s" % str(sourcefilename))
       
   333 
       
   334 		except Exception,e:
       
   335 			raise MetaDataError("Preprocessor exception: %s" % str(e))
       
   336 
       
   337 		return 0	# all OK
       
   338 
       
   339 	def setMacros(self, aMacros):
       
   340 		self.__Macros = aMacros
       
   341 
       
   342 	def addMacro(self, aMacro):
       
   343 		self.__Macros.append(aMacro)
       
   344 
       
   345 	def addMacros(self, aMacros):
       
   346 		self.__Macros.extend(aMacros)
       
   347 
       
   348 	def getMacros(self):
       
   349 		return self.__Macros
       
   350 
       
   351 
       
   352 	def addIncludePath(self, aIncludePath):
       
   353 		p = str(aIncludePath)
       
   354 		if p == "":
       
   355 			self.raptor.Warn("attempt to set an empty preprocessor include path for %s" % str(self.filename))
       
   356 		else:
       
   357 			self.__IncludePaths.append(p)
       
   358 
       
   359 	def addIncludePaths(self, aIncludePaths):
       
   360 		for path in aIncludePaths:
       
   361 			self.addIncludePath(path)
       
   362 
       
   363 	def setIncludePaths(self, aIncludePaths):
       
   364 		self.__IncludePaths = []
       
   365 		self.addIncludePaths(aIncludePaths)
       
   366 
       
   367 	def setPreIncludeFile(self, aPreIncludeFile):
       
   368 		self.__PreIncludeFile = aPreIncludeFile
       
   369 
       
   370 	def preprocess(self):
       
   371 		preProcessorCall = self.__constructPreProcessorCall()
       
   372 		returnValue = self.call(preProcessorCall, self.filename)
       
   373 
       
   374 		return self.getOutput()
       
   375 
       
   376 	def __constructPreProcessorCall(self):
       
   377 
       
   378 		call = self.__StaticOptions
       
   379 
       
   380 		if self.__PreIncludeFile:
       
   381 			call += " " + self.__PreIncludeOption
       
   382 			call += " " + str(self.__PreIncludeFile)
       
   383 
       
   384 		for macro in self.__Macros:
       
   385 			call += " " + self.__MacroOption + macro
       
   386 
       
   387 		for includePath in self.__IncludePaths:
       
   388 			call += " " + self.__IncludeOption
       
   389 			call += " " + str(includePath)
       
   390 
       
   391 		return call
       
   392 
       
   393 
       
   394 class MetaDataFile(object):
       
   395 	"""A generic representation of a Symbian metadata file
       
   396 
       
   397 	Symbian metadata files are subject to preprocessing, primarily with macros based
       
   398 	on the selected build platform.  This class provides a generic means of wrapping
       
   399 	up the preprocessing of such files."""
       
   400 
       
   401 	def __init__(self, aFilename, gnucpp, aRootLocation=None, log=None):
       
   402 		"""
       
   403 		@param aFilename	An MMP, bld.inf or other preprocessable build spec file
       
   404 		@param aDefaultPlatform  Default preprocessed version of this file
       
   405 		@param aCPP 		location of GNU CPP
       
   406 		@param log 		A class with Debug(<string>), Info(<string>) and Error(<string>) methods
       
   407 		"""
       
   408 		self.filename = aFilename
       
   409 		self.__RootLocation = aRootLocation
       
   410 		# Dictionary with key of build platform and a text string of processed output as values
       
   411 		self.__PreProcessedContent = {}
       
   412 		self.log = log
       
   413 
       
   414 		self.__gnucpp = gnucpp
       
   415 		if gnucpp is None:
       
   416 			raise ValueError('gnucpp must be set')
       
   417 
       
   418 	def depspath(self, platform):
       
   419 	   """ Where does dependency information go relative to platform's SBS_BUILD_DIR?
       
   420 	       Subclasses should redefine this
       
   421 	   """
       
   422 	   return str(platform['SBS_BUILD_DIR']) + "/" + str(self.__RootLocation) + "." + platform['key_md5'] + ".d"
       
   423 
       
   424 	def getContent(self, aBuildPlatform):
       
   425 
       
   426 		key = aBuildPlatform['key']
       
   427 
       
   428 		config_macros = []
       
   429 
       
   430 		adepfilename = self.depspath(aBuildPlatform)
       
   431 		generateDepsOptions = ""
       
   432 		if adepfilename:
       
   433 
       
   434 			if raptor_utilities.getOSPlatform().startswith("win"):
       
   435 				metatarget = "$(PARSETARGET)"
       
   436 			else:
       
   437 				metatarget = "'$(PARSETARGET)'"
       
   438 			generateDepsOptions = "-MD -MF%s -MT%s" % (adepfilename, metatarget)
       
   439 			aBuildPlatform['METADEPS'].append((adepfilename, metatarget))
       
   440 			try:
       
   441 				os.makedirs(os.path.dirname(adepfilename))
       
   442 			except Exception, e:
       
   443 				self.log.Debug("Couldn't make bldinf outputpath for dependency generation")
       
   444 
       
   445 		config_macros = (aBuildPlatform['PLATMACROS']).split()
       
   446 
       
   447 		if not key in self.__PreProcessedContent:
       
   448 
       
   449 			preProcessor = PreProcessor(self.__gnucpp, '-undef -nostdinc ' + generateDepsOptions + ' ',
       
   450 										'-I', '-D', '-include', self.log)
       
   451 			preProcessor.filename = self.filename
       
   452 
       
   453 			# always have the current directory on the include path
       
   454 			preProcessor.addIncludePath('.')
       
   455 
       
   456 			# the SYSTEMINCLUDE directories defined in the build config
       
   457 			# should be on the include path. This is added mainly to support
       
   458 			# Feature Variation as SYSTEMINCLUDE is usually empty at this point.
       
   459 			systemIncludes = aBuildPlatform['SYSTEMINCLUDE']
       
   460 			if systemIncludes:
       
   461 				preProcessor.addIncludePaths(systemIncludes.split())
       
   462 
       
   463 			preInclude = aBuildPlatform['VARIANT_HRH']
       
   464 
       
   465 			# for non-Feature Variant builds, the directory containing the HRH should
       
   466 			# be on the include path
       
   467 			if not aBuildPlatform['ISFEATUREVARIANT']:
       
   468 				preProcessor.addIncludePath(preInclude.Dir())
       
   469 
       
   470 			# and EPOCROOT/epoc32/include
       
   471 			preProcessor.addIncludePath(aBuildPlatform['EPOCROOT'].Append('epoc32/include'))
       
   472 
       
   473 			# and the directory containing the bld.inf file
       
   474 			if self.__RootLocation is not None and str(self.__RootLocation) != "":
       
   475 				preProcessor.addIncludePath(self.__RootLocation)
       
   476 
       
   477 			# and the directory containing the file we are processing
       
   478 			preProcessor.addIncludePath(self.filename.Dir())
       
   479 
       
   480 			# there is always a pre-include file
       
   481 			preProcessor.setPreIncludeFile(preInclude)
       
   482 
       
   483 			macros = ["SBSV2"]
       
   484 
       
   485 			if config_macros:
       
   486 				macros.extend(config_macros)
       
   487 
       
   488 			if macros:
       
   489 				for macro in macros:
       
   490 					preProcessor.addMacro(macro + "=_____" +macro)
       
   491 
       
   492 			# extra "raw" macros that do not need protecting
       
   493 			preProcessor.addMacro("__GNUC__=3")
       
   494 
       
   495 			preProcessorOutput = preProcessor.preprocess()
       
   496 
       
   497 			# Resurrect preprocessing replacements
       
   498 			pattern = r'([\\|/]| |) ?_____(('+macros[0]+')'
       
   499 			for macro in macros[1:]:
       
   500 				pattern += r'|('+macro+r')'
       
   501 
       
   502 			pattern += r'\s*)'
       
   503 			# Work on all Macros in one substitution.
       
   504 			text = re.sub(pattern, r"\1\2", preProcessorOutput)
       
   505 			text = re.sub(r"\n[\t ]*", r"\n", text)
       
   506 
       
   507 			self.__PreProcessedContent[key] = text
       
   508 
       
   509 		return self.__PreProcessedContent[key]
       
   510 
       
   511 class MMPFile(MetaDataFile):
       
   512 	"""A generic representation of a Symbian metadata file
       
   513 
       
   514 	Symbian metadata files are subject to preprocessing, primarily with macros based
       
   515 	on the selected build platform.  This class provides a generic means of wrapping
       
   516 	up the preprocessing of such files."""
       
   517 
       
   518 	def __init__(self, aFilename, gnucpp, bldinf, log=None):
       
   519 		"""
       
   520 		@param aFilename	An MMP, bld.inf or other preprocessable build spec file
       
   521 		@param gnucpp 		location of GNU CPP
       
   522 		@param bldinf   	the bldinf file that this mmp comes from
       
   523 		@param log 			A class with Debug(<string>), Info(<string>) and Error(<string>) methods
       
   524 		"""
       
   525 		super(MMPFile, self).__init__(aFilename, gnucpp, str(bldinf.filename.Dir()), log)
       
   526 		self.__bldinf = bldinf
       
   527 
       
   528 		self.__gnucpp = gnucpp
       
   529 		if gnucpp is None:
       
   530 			raise ValueError('gnucpp must be set')
       
   531 
       
   532 	def depspath(self, platform):
       
   533 	   """ Where does dependency information go relative to platform's SBS_BUILD_DIR?
       
   534 	       Subclasses should redefine this
       
   535 	   """
       
   536 	   return self.__bldinf.outputpath(platform) + "/" + self.filename.File() + '.' + platform['key_md5'] + ".d"
       
   537 
       
   538 class Export(object):
       
   539 	"""Single processed PRJ_EXPORTS or PRJ_TESTEXPORTS entry from a bld.inf file"""
       
   540 
       
   541 	def getPossiblyQuotedStrings(cls,spec):
       
   542 		""" 	Split a string based on whitespace
       
   543 			but keep double quoted substrings together.
       
   544 		"""
       
   545 		inquotes=False
       
   546 		intokengap=False
       
   547 		sourcedest=[]
       
   548 		word = 0
       
   549 		for c in spec:
       
   550 			if c == '"':
       
   551 				if inquotes:
       
   552 					inquotes = False
       
   553 					word += 1
       
   554 					intokengap = True
       
   555 				else:
       
   556 					inquotes = True
       
   557 					intokengap = False
       
   558 				pass
       
   559 			elif c == ' ' or c == '\t':
       
   560 				if inquotes:
       
   561 					if len(sourcedest) == word:
       
   562 						sourcedest.append(c)
       
   563 					else:
       
   564 						sourcedest[word] += c
       
   565 				else:
       
   566 					if intokengap:
       
   567 						# gobble unquoted spaces
       
   568 						pass
       
   569 					else:
       
   570 						word += 1
       
   571 						intokengap=True
       
   572 				pass
       
   573 			else:
       
   574 				intokengap = False
       
   575 				if len(sourcedest) == word:
       
   576 					sourcedest.append(c)
       
   577 				else:
       
   578 					sourcedest[word] += c
       
   579 
       
   580 		return sourcedest
       
   581 
       
   582 	getPossiblyQuotedStrings = classmethod(getPossiblyQuotedStrings)
       
   583 
       
   584 
       
   585 	def __init__(self, aBldInfFile, aExportsLine, aType):
       
   586 		"""
       
   587 		Rules from the OS library for convenience:
       
   588 
       
   589 		For PRJ_TESTEXPORTS
       
   590 		source_file_1 [destination_file]
       
   591 		source_file_n [destination_file]
       
   592 		If the source file is listed with a relative path, the path will
       
   593 	 	  be considered relative to the directory containing the bld.inf file.
       
   594 		If a destination file is not specified, the source file will be copied
       
   595 		  to the directory containing the bld.inf file.
       
   596 		If a relative path is specified with the destination file, the path
       
   597 		  will be considered relative to directory containing the bld.inf file.
       
   598 
       
   599 		For PRJ_EXPORTS
       
   600 		source_file_1 [destination_file]
       
   601 		source_file_n [destination_file]
       
   602 		:zip zip_file [destination_path]
       
   603 
       
   604 		Note that:
       
   605 		If a source file is listed with a relative path, the path will be
       
   606 		considered relative to the directory containing the bld.inf file.
       
   607 
       
   608 		If a destination file is not specified, the source file will be copied
       
   609 		to epoc32\include\.
       
   610 
       
   611 		If a destination file is specified with the relative path, the path will
       
   612 		be considered relative to directory epoc32\include\.
       
   613 
       
   614 		If a destination begins with a drive letter, then the file is copied to
       
   615 		epoc32\data\<drive_letter>\<path>. For example,
       
   616 
       
   617 			mydata.dat e:\appdata\mydata.dat
       
   618 			copies mydata.dat to epoc32\data\e\appdata\mydata.dat.
       
   619 			You can use any driveletter between A and Z.
       
   620 
       
   621 		A line can start with the preface :zip. This instructs the build tools
       
   622 		to unzip the specified zip file to the specified destination path. If a
       
   623 		destination path is not specified, the source file will be unzipped in
       
   624 		the root directory.
       
   625 
       
   626 
       
   627 		"""
       
   628 
       
   629 		# Work out what action is required - unzip or copy?
       
   630 		action = "copy"
       
   631 		typematch = re.match(r'^\s*(?P<type>:zip\s+)?(?P<spec>[^\s].*[^\s])\s*$',aExportsLine, re.I)
       
   632 
       
   633 		spec = typematch.group('spec')
       
   634 		if spec == None:
       
   635 			raise ValueError('must specify at least a source file for an export')
       
   636 
       
   637 		if typematch.group('type') is not None:
       
   638 			action = "unzip"
       
   639 
       
   640 		# Split the spec into source and destination but take care
       
   641 		# to allow filenames with quoted strings.
       
   642 		exportEntries = Export.getPossiblyQuotedStrings(spec)
       
   643 
       
   644 		# Get the source path as specified by the bld.inf
       
   645 		source_spec = exportEntries.pop(0).replace(' ','%20')
       
   646 
       
   647 		# Resolve the source file
       
   648 		sourcepath = generic_path.Path(raptor_utilities.resolveSymbianPath(str(aBldInfFile), source_spec))
       
   649 
       
   650 		# Find it if the case of the filename is wrong:
       
   651 		# Carry on even if we don't find it
       
   652 		foundfile = sourcepath.FindCaseless()
       
   653 		if foundfile != None:
       
   654 			source = str(foundfile).replace(' ','%20')
       
   655 		else:
       
   656 			source = str(sourcepath).replace(' ','%20')
       
   657 
       
   658 
       
   659 		# Get the destination path as specified by the bld.inf
       
   660 		if len(exportEntries) > 0:
       
   661 			dest_spec = exportEntries.pop(0).replace(' ','%20')
       
   662 		else:
       
   663 			dest_spec = None
       
   664 		# Destination list - list of destinations. For the WINSCW resource building stage,
       
   665 		# files exported to the emulated drives and there are several locations, for example,
       
   666 		# PRJ_[TEST]EXPORTS
       
   667 		# 1234ABCD.SPD		z:/private/10009876/policy/1234ABCD.spd
       
   668 		# needs to end up copied in
       
   669 		# epoc32/data/z/private/10009876/policy/1234ABCD.spd *and* in
       
   670 		# epoc32/release/winscw/udeb/z/private/10009876/policy/1234ABCD.spd *and* in
       
   671 		# epoc32/release/winscw/urel/z/private/10009876/policy/1234ABCD.spd
       
   672 		dest_list = []
       
   673 
       
   674 		# Resolve the destination if one is specified
       
   675 		if dest_spec:
       
   676 			# check for troublesome characters
       
   677 			if ':' in dest_spec and not re.search('^[a-z]:', dest_spec, re.I):
       
   678 				raise ValueError("invalid filename " + dest_spec)
       
   679 
       
   680 			dest_spec = dest_spec.replace(' ','%20')
       
   681 			aSubType=""
       
   682 			if action == "unzip":
       
   683 				aSubType=":zip"
       
   684 				dest_spec = dest_spec.rstrip("\\/")
       
   685 
       
   686 			# Get the export destination(s) - note this can be a list of strings or just a string.
       
   687 			dest_list = raptor_utilities.resolveSymbianPath(str(aBldInfFile), dest_spec, aType, aSubType)
       
   688 
       
   689 			def process_dest(aDest):
       
   690 				if dest_spec.endswith('/') or  dest_spec.endswith('\\'):
       
   691 					m = generic_path.Path(source)
       
   692 					aDest += '/'+m.File()
       
   693 				return aDest
       
   694 
       
   695 			if isinstance(dest_list, list):
       
   696 				# Process each file in the list
       
   697 				dest_list = map(process_dest, dest_list)
       
   698 			else:
       
   699 				# Process the single destination
       
   700 				dest_list = process_dest(dest_list)
       
   701 
       
   702 		else:
       
   703 			# No destination was specified so we assume an appropriate one
       
   704 
       
   705 			dest_filename=generic_path.Path(source).File()
       
   706 
       
   707 			if aType == "PRJ_EXPORTS":
       
   708 				if action == "copy":
       
   709 					destination = '$(EPOCROOT)/epoc32/include/'+dest_filename
       
   710 				elif action == "unzip":
       
   711 					destination = '$(EPOCROOT)'
       
   712 			elif aType == "PRJ_TESTEXPORTS":
       
   713 				d = aBldInfFile.Dir()
       
   714 				if action == "copy":
       
   715 					destination = str(d.Append(dest_filename))
       
   716 				elif action == "unzip":
       
   717 					destination = "$(EPOCROOT)"
       
   718 			else:
       
   719 				raise ValueError("Export type should be 'PRJ_EXPORTS' or 'PRJ_TESTEXPORTS'. It was: "+str(aType))
       
   720 
       
   721 
       
   722 		self.__Source = source
       
   723 		if len(dest_list) > 0: # If the list has length > 0, this means there are several export destinations.
       
   724 			self.__Destination = dest_list
       
   725 		else: # Otherwise the list has length zero, so there is only a single export destination.
       
   726 			self.__Destination = destination
       
   727 		self.__Action = action
       
   728 
       
   729 	def getSource(self):
       
   730 		return self.__Source
       
   731 
       
   732 	def getDestination(self):
       
   733 		return self.__Destination # Note that this could be either a list, or a string, depending on the export destination
       
   734 
       
   735 	def getAction(self):
       
   736 		return self.__Action
       
   737 
       
   738 class ExtensionmakefileEntry(object):
       
   739 	def __init__(self, aGnuLine, aBldInfFile, tmp):
       
   740 
       
   741 		self.__BldInfFile = aBldInfFile
       
   742 		bldInfLocation = self.__BldInfFile.Dir()
       
   743 		biloc = str(bldInfLocation)
       
   744 		extInfLocation = tmp.filename.Dir()
       
   745 		eiloc = str(extInfLocation)
       
   746 
       
   747 		if eiloc is None or eiloc == "":
       
   748 			eiloc="." # Someone building with a relative raptor path
       
   749 		if biloc is None or biloc == "":
       
   750 			biloc="." # Someone building with a relative raptor path
       
   751 
       
   752 		self.__StandardVariables = {}
       
   753 		# Relative step-down to the root - let's try ignoring this for now, as it
       
   754 		# should amount to the same thing in a world where absolute paths are king
       
   755 		self.__StandardVariables['TO_ROOT'] = ""
       
   756 		# Top-level bld.inf location
       
   757 		self.__StandardVariables['TO_BLDINF'] = biloc
       
   758 		self.__StandardVariables['EXTENSION_ROOT'] = eiloc
       
   759 
       
   760 		# Get the directory and filename from the full path containing the extension makefile
       
   761 		self.__FullPath = generic_path.Join(eiloc,aGnuLine)
       
   762 		self.__FullPath = self.__FullPath.GetLocalString()
       
   763 		self.__Filename = os.path.split(self.__FullPath)[1]
       
   764 		self.__Directory = os.path.split(self.__FullPath)[0]
       
   765 
       
   766 	def getMakefileName(self):
       
   767 		return self.__Filename
       
   768 
       
   769 	def getMakeDirectory(self):
       
   770 		return self.__Directory
       
   771 
       
   772 	def getStandardVariables(self):
       
   773 		return self.__StandardVariables
       
   774 
       
   775 class Extension(object):
       
   776 	"""Single processed PRJ_EXTENSIONS or PRJ_TESTEXTENSIONS START EXTENSIONS...END block
       
   777 	from a bld.inf file"""
       
   778 
       
   779 	def __init__(self, aBldInfFile, aStartLine, aOptionLines, aBuildPlatform, aRaptor):
       
   780 		self.__BldInfFile = aBldInfFile
       
   781 		self.__Options = {}
       
   782 		self.interface = ""
       
   783 		self.__Raptor = aRaptor
       
   784 
       
   785 		makefile = ""
       
   786 		makefileMatch = re.search(r'^\s*START EXTENSION\s+(?P<MAKEFILE>\S+)\s*(?P<NAMETAG>\S*)$', aStartLine, re.I)
       
   787 
       
   788 		self.__RawMakefile = ""
       
   789 
       
   790 		if (makefileMatch):
       
   791 			self.__RawMakefile = makefileMatch.group('MAKEFILE')
       
   792 			self.nametag = makefileMatch.group('NAMETAG').lower()
       
   793 
       
   794 			# Ensure all \'s are translated into /'s if required
       
   795 			self.interface = self.__RawMakefile
       
   796 			self.interface = self.interface.replace("\\", "/").replace("/", ".")
       
   797 
       
   798 		# To support standalone testing, '$(' prefixed TEMs  are assumed to  start with
       
   799 		# a makefile variable and hence be fully located in FLM operation
       
   800 		if self.__RawMakefile.startswith("$("):
       
   801 			self.__Makefile = self.__RawMakefile + ".mk"
       
   802 		else:
       
   803 			self.__Makefile = '$(MAKEFILE_TEMPLATES)/' + self.__RawMakefile + ".mk"
       
   804 
       
   805 		for optionLine in aOptionLines:
       
   806 			optionMatch = re.search(r'^\s*(OPTION\s+)?(?P<VARIABLE>\S+)\s+(?P<VALUE>\S+.*)$',optionLine, re.I)
       
   807 			if optionMatch:
       
   808 				self.__Options[optionMatch.group('VARIABLE').upper()] = optionMatch.group('VALUE')
       
   809 
       
   810 		bldInfLocation = self.__BldInfFile.Dir()
       
   811 
       
   812 		biloc = str(bldInfLocation)
       
   813 		if biloc is None or biloc == "":
       
   814 			biloc="." # Someone building with a relative raptor path
       
   815 
       
   816 		extInfLocation = aStartLine.filename.Dir()
       
   817 
       
   818 		eiloc = str(extInfLocation)
       
   819 		if eiloc is None or eiloc == "":
       
   820 			eiloc="." # Someone building with a relative raptor path
       
   821 
       
   822 		self.__StandardVariables = {}
       
   823 		# Relative step-down to the root - let's try ignoring this for now, as it
       
   824 		# should amount to the same thing in a world where absolute paths are king
       
   825 		self.__StandardVariables['TO_ROOT'] = ""
       
   826 		# Top-level bld.inf location
       
   827 		self.__StandardVariables['TO_BLDINF'] = biloc
       
   828 		# Location of bld.inf file containing the current EXTENSION block
       
   829 		self.__StandardVariables['EXTENSION_ROOT'] = eiloc
       
   830 
       
   831 		# If the interface exists, this means it's not a Template Extension Makefile so don't look for a .meta file for it;
       
   832 		# so do nothing if it's not a template extension makefile
       
   833 		try:
       
   834 			self.__Raptor.cache.FindNamedInterface(str(self.interface), aBuildPlatform['CACHEID'])
       
   835 		except KeyError: # This means that this Raptor doesn't have the interface self.interface, so we are in a TEM
       
   836 			# Read extension meta file and get default options from it.  The use of TEM meta file is compulsory if TEM is used
       
   837 			metaFilename = "%s/epoc32/tools/makefile_templates/%s.meta" % (aBuildPlatform['EPOCROOT'], self.__RawMakefile)
       
   838 			metaFile = None
       
   839 			try:
       
   840 				metaFile = open(metaFilename, "r")
       
   841 			except IOError, e:
       
   842 				self.__warn("Extension: %s - cannot open Meta file: %s" % (self.__RawMakefile, metaFilename))
       
   843 
       
   844 			if metaFile:
       
   845 				for line in metaFile.readlines():
       
   846 					defaultOptionMatch = re.search(r'^OPTION\s+(?P<VARIABLE>\S+)\s+(?P<VALUE>\S+.*)$',line, re.I)
       
   847 					if defaultOptionMatch and defaultOptionMatch.group('VARIABLE').upper() not in self.__Options.keys():
       
   848 						self.__Options[defaultOptionMatch.group('VARIABLE').upper()] = defaultOptionMatch.group('VALUE')
       
   849 
       
   850 				metaFile.close()
       
   851 
       
   852 	def __warn(self, format, *extras):
       
   853 		if (self.__Raptor):
       
   854 			self.__Raptor.Warn(format, *extras)
       
   855 
       
   856 	def getIdentifier(self):
       
   857 		return re.sub (r'\\|\/|\$|\(|\)', '_', self.__RawMakefile)
       
   858 
       
   859 	def getMakefile(self):
       
   860 		return self.__Makefile
       
   861 
       
   862 	def getOptions(self):
       
   863 		return self.__Options
       
   864 
       
   865 	def getStandardVariables(self):
       
   866 		return self.__StandardVariables
       
   867 
       
   868 class MMPFileEntry(object):
       
   869 	def __init__(self, aFilename, aTestOption, aARMOption):
       
   870 		self.filename = aFilename
       
   871 		self.testoption = aTestOption
       
   872 		if aARMOption:
       
   873 			self.armoption = True
       
   874 		else:
       
   875 			self.armoption = False
       
   876 
       
   877 
       
   878 class BldInfFile(MetaDataFile):
       
   879 	"""Representation of a Symbian bld.inf file"""
       
   880 
       
   881 	def __init__(self, aFilename, gnucpp, log=None):
       
   882 		MetaDataFile.__init__(self, aFilename, gnucpp, None, log)
       
   883 		self.__Raptor = log
       
   884 		self.testManual = 0
       
   885 		self.testAuto = 0
       
   886 	# Generic
       
   887 
       
   888 	def getBuildPlatforms(self, aBuildPlatform):
       
   889 		platformList = []
       
   890 
       
   891 		for platformLine in self.__getSection(aBuildPlatform, 'PRJ_PLATFORMS'):
       
   892 			for platformEntry in platformLine.split():
       
   893 				platformList.append(platformEntry)
       
   894 
       
   895 		return platformList
       
   896 
       
   897 	# Build Platform Specific
       
   898 	def getMMPList(self, aBuildPlatform, aType="PRJ_MMPFILES"):
       
   899 		mmpFileList=[]
       
   900 		gnuList = []
       
   901 		makefileList = []
       
   902 		extFound = False
       
   903 		m = None
       
   904 
       
   905 		hashValue = {'mmpFileList': [] , 'gnuList': [], 'makefileList' : []}
       
   906 
       
   907 		for mmpFileEntry in self.__getSection(aBuildPlatform, aType):
       
   908 
       
   909 			actualBldInfRoot = mmpFileEntry.getFilename()
       
   910 			n = re.match('\s*(?P<makefiletype>(GNUMAKEFILE|N?MAKEFILE))\s+(?P<extmakefile>[^ ]+)\s*(support|manual)?\s*(?P<invalid>\S+.*)?\s*$',mmpFileEntry,re.I)
       
   911 			if n:
       
   912 
       
   913 				if (n.groupdict()['invalid']):
       
   914 					self.log.Error("%s (%d) : invalid .mmp file qualifier \"%s\"", mmpFileEntry.filename, mmpFileEntry.getLineNumber(), n.groupdict()['invalid'])
       
   915 				if raptor_utilities.getOSFileSystem() == "unix":
       
   916 					self.log.Warn("NMAKEFILE/GNUMAKEFILE/MAKEFILE keywords not supported on Linux")
       
   917 				else:
       
   918 					extmakefilearg = n.groupdict()['extmakefile']
       
   919 					bldInfDir = actualBldInfRoot.Dir()
       
   920 					extmakefilename = bldInfDir.Append(extmakefilearg)
       
   921 					extmakefile = ExtensionmakefileEntry(extmakefilearg, self.filename, mmpFileEntry)
       
   922 
       
   923 					if (n.groupdict()['makefiletype']).upper() == "GNUMAKEFILE":
       
   924 						gnuList.append(extmakefile)
       
   925 					else:
       
   926 						makefileList.append(extmakefile)
       
   927 			else:
       
   928 				# Currently there is only one possible option - build as arm.
       
   929 				# For TESTMMPFILES, the supported options are support, tidy, ignore, manual and build as arm
       
   930 				if aType.upper()=="PRJ_TESTMMPFILES":
       
   931 					if re.match('\s*(?P<name>[^ ]+)\s*(?P<baa>build_as_arm)?\s*(?P<support>support)?\s*(?P<ignore>ignore)?\s*(?P<tidy>tidy)?\s*(?P<manual>manual)?\s*(?P<invalid>\S+.*)?\s*$', mmpFileEntry, re.I):
       
   932 						m = re.match('\s*(?P<name>[^ ]+)\s*(?P<baa>build_as_arm)?\s*(?P<support>support)?\s*(?P<ignore>ignore)?\s*(?P<tidy>tidy)?\s*(?P<manual>manual)?\s*(?P<invalid>\S+.*)?\s*$', mmpFileEntry, re.I)
       
   933 				else:
       
   934 					if re.match('\s*(?P<name>[^ ]+)\s*(?P<baa>build_as_arm)?\s*(?P<invalid>\S+.*)?\s*$', mmpFileEntry, re.I):
       
   935 						m = re.match('\s*(?P<name>[^ ]+)\s*(?P<baa>build_as_arm)?\s*(?P<invalid>\S+.*)?\s*$', mmpFileEntry, re.I)
       
   936 
       
   937 			if m:
       
   938 				if (m.groupdict()['invalid']):
       
   939 					self.log.Error("%s (%d) : invalid .mmp file qualifier \"%s\"", mmpFileEntry.filename, mmpFileEntry.getLineNumber(), m.groupdict()['invalid'])
       
   940 
       
   941 				mmpFileName = m.groupdict()['name']
       
   942 				testmmpoption = "auto" # Setup tests to be automatic by default
       
   943 				tokens = m.groupdict()
       
   944 				for key,item in tokens.iteritems():
       
   945 					if key=="manual" and item=="manual":
       
   946 						testmmpoption = "manual"
       
   947 					elif key=="support" and item=="support":
       
   948 						testmmpoption = "support"
       
   949 					elif key=="ignore" and item=="ignore":
       
   950 						testmmpoption = "ignore"
       
   951 
       
   952 				buildasarm = False
       
   953 				if  m.groupdict()['baa']:
       
   954 					if m.groupdict()['baa'].lower() == 'build_as_arm':
       
   955 						buildasarm = True
       
   956 
       
   957 				if not mmpFileName.lower().endswith('.mmp'):
       
   958 					mmpFileName += '.mmp'
       
   959 				bldInfDir = actualBldInfRoot.Dir()
       
   960 				try:
       
   961 					mmpFileName = bldInfDir.Append(mmpFileName)
       
   962 					mmpfe = MMPFileEntry(mmpFileName, testmmpoption, buildasarm)
       
   963 					mmpFileList.append(mmpfe)
       
   964 				except ValueError, e:
       
   965 					self.log.Error("invalid .mmp file name: %s" % str(e))
       
   966 
       
   967 				m = None
       
   968 
       
   969 
       
   970 		hashValue['mmpFileList'] = mmpFileList
       
   971 		hashValue['gnuList'] = gnuList
       
   972 		hashValue['makefileList'] = makefileList
       
   973 
       
   974 		return hashValue
       
   975 
       
   976 	# Return a list of gnumakefiles used in the bld.inf
       
   977 	def getExtensionmakefileList(self, aBuildPlatform, aType="PRJ_MMPFILES",aString = ""):
       
   978 		extMakefileList=[]
       
   979 		m = None
       
   980 		for extmakeFileEntry in self.__getSection(aBuildPlatform, aType):
       
   981 
       
   982 			actualBldInfRoot = extmakeFileEntry.filename
       
   983 			if aType.upper()=="PRJ_TESTMMPFILES":
       
   984 				m = re.match('\s*GNUMAKEFILE\s+(?P<extmakefile>[^ ]+)\s*(?P<support>support)?\s*(?P<ignore>ignore)?\s*(?P<tidy>tidy)?\s*(?P<manual>manual)?\s*(?P<invalid>\S+.*)?\s*$',extmakeFileEntry,re.I)
       
   985 			else:
       
   986 				if aString == "gnumakefile":
       
   987 					m = re.match('\s*GNUMAKEFILE\s+(?P<extmakefile>[^ ]+)\s*(?P<invalid>\S+.*)?\s*$',extmakeFileEntry,re.I)
       
   988 				elif aString == "nmakefile":
       
   989 					m = re.match('\s*NMAKEFILE\s+(?P<extmakefile>[^ ]+)\s*(?P<invalid>\S+.*)?\s*$',extmakeFileEntry,re.I)
       
   990 				elif aString == "makefile":
       
   991 					m = re.match('\s*MAKEFILE\s+(?P<extmakefile>[^ ]+)\s*(?P<invalid>\S+.*)?\s*$',extmakeFileEntry,re.I)
       
   992 			if m:
       
   993 				if (m.groupdict()['invalid']):
       
   994 					self.log.Error("%s (%d) : invalid extension makefile qualifier \"%s\"", extmakeFileEntry.filename, extmakeFileEntry.getLineNumber(), m.groupdict()['invalid'])
       
   995 
       
   996 				extmakefilearg = m.groupdict()['extmakefile']
       
   997 				bldInfDir = actualBldInfRoot.Dir()
       
   998 				extmakefilename = bldInfDir.Append(extmakefilearg)
       
   999 				extmakefile = ExtensionmakefileEntry(extmakefilearg, self.filename, extmakeFileEntry)
       
  1000 				extMakefileList.append(extmakefile)
       
  1001 				m = None
       
  1002 
       
  1003 		return extMakefileList
       
  1004 
       
  1005 	def getTestExtensionmakefileList(self,aBuildPlatform,aString=""):
       
  1006 		return self.getExtensionmakefileList(aBuildPlatform,"PRJ_TESTMMPFILES",aString)
       
  1007 
       
  1008 	def getTestMMPList(self, aBuildPlatform):
       
  1009 		return self.getMMPList(aBuildPlatform, "PRJ_TESTMMPFILES")
       
  1010 
       
  1011 	def getRomTestType(self, aBuildPlatform):
       
  1012 		testMMPList = self.getTestMMPList(aBuildPlatform)
       
  1013 		for testMMPFileEntry in testMMPList['mmpFileList']:
       
  1014 			if aBuildPlatform["TESTCODE"]:
       
  1015 				# Calculate test type (manual or auto)
       
  1016 				if testMMPFileEntry.testoption == "manual":
       
  1017 					self.testManual += 1
       
  1018 				if not (testMMPFileEntry.testoption == "support" or testMMPFileEntry.testoption == "manual" or testMMPFileEntry.testoption == "ignore"):
       
  1019 					self.testAuto += 1
       
  1020 		if self.testManual and self.testAuto:
       
  1021 			return 'BOTH'
       
  1022 		elif self.testAuto:
       
  1023 			return 'AUTO'
       
  1024 		elif self.testManual:
       
  1025 			return 'MANUAL'
       
  1026 		else:
       
  1027 			return 'NONE'
       
  1028 
       
  1029 	def getExports(self, aBuildPlatform, aType="PRJ_EXPORTS"):
       
  1030 		exportList = []
       
  1031 
       
  1032 		for exportLine in self.__getSection(aBuildPlatform, aType):
       
  1033 
       
  1034 			if not re.match(r'\S+', exportLine):
       
  1035 				continue
       
  1036 
       
  1037 			try:
       
  1038 				exportList.append(Export(exportLine.getFilename(), exportLine, aType))
       
  1039 			except ValueError,e:
       
  1040 				self.log.Error(str(e))
       
  1041 
       
  1042 		return exportList
       
  1043 
       
  1044 	def getTestExports(self, aBuildPlatform):
       
  1045 		return self.getExports(aBuildPlatform, "PRJ_TESTEXPORTS")
       
  1046 
       
  1047 	def getExtensions(self, aBuildPlatform, aType="PRJ_EXTENSIONS"):
       
  1048 		extensionObjects = []
       
  1049 		start = ""
       
  1050 		options = []
       
  1051 
       
  1052 		for extensionLine in self.__getSection(aBuildPlatform, aType):
       
  1053 			if (re.search(r'^\s*START ',extensionLine, re.I)):
       
  1054 				start = extensionLine
       
  1055 			elif re.search(r'^\s*END\s*$',extensionLine, re.I):
       
  1056 				extensionObjects.append(Extension(self.filename, start, options, aBuildPlatform, self.__Raptor))
       
  1057 				start = ""
       
  1058 				options = []
       
  1059 			elif re.search(r'^\s*$',extensionLine, re.I):
       
  1060 				continue
       
  1061 			elif start:
       
  1062 				options.append(extensionLine)
       
  1063 
       
  1064 		return extensionObjects
       
  1065 
       
  1066 	def getTestExtensions(self, aBuildPlatform):
       
  1067 		return self.getExtensions(aBuildPlatform, "PRJ_TESTEXTENSIONS")
       
  1068 
       
  1069 	def __getSection(self, aBuildPlatform, aSection):
       
  1070 
       
  1071 		activeSection = False
       
  1072 		sectionContent = []
       
  1073 		lineContent = re.split(r'\n', self.getContent(aBuildPlatform));
       
  1074 
       
  1075 		currentBldInfFile = self.filename
       
  1076 		currentLineNumber = 0
       
  1077 
       
  1078 		for line in lineContent:
       
  1079 			if line.startswith("#"):
       
  1080 				commentDetail = getPreProcessorCommentDetail(line)
       
  1081 				currentBldInfFile = commentDetail[0]
       
  1082 				currentLineNumber = commentDetail[1]-1
       
  1083 				continue
       
  1084 
       
  1085 			currentLineNumber += 1
       
  1086 
       
  1087 			if not re.match(r'.*\S+', line):
       
  1088 				continue
       
  1089 			elif re.match(r'\s*' + aSection + r'\s*$', line, re.I):
       
  1090 				activeSection = True
       
  1091 			elif re.match(r'\s*PRJ_\w+\s*$', line, re.I):
       
  1092 				activeSection = False
       
  1093 			elif activeSection:
       
  1094 				sectionContent.append(PreProcessedLine(line, currentBldInfFile, currentLineNumber))
       
  1095 
       
  1096 		return sectionContent
       
  1097 
       
  1098 	@staticmethod
       
  1099 	def outputPathFragment(bldinfpath):
       
  1100 		"""Return a relative path that uniquely identifies this bldinf file
       
  1101 		   whilst being short so that it can be appended to epoc32/build.
       
  1102 		   The  build product of a particular bld.inf may be placed in here.
       
  1103 		   This affects its TEMs and its MMPs"""
       
  1104 
       
  1105 		absroot_str = os.path.abspath(str(bldinfpath)).lower().replace("\\","/")
       
  1106 
       
  1107 		uniqueid = hashlib.md5()
       
  1108 		uniqueid.update(absroot_str)
       
  1109 
       
  1110 		specnamecomponents = (re.sub("^[A-Za-z]:", "", absroot_str)).split('/') # split, removing any drive identifier (if present)
       
  1111 
       
  1112 		pathlist=[]
       
  1113 		while len(specnamecomponents) > 0:
       
  1114 			top = specnamecomponents.pop()
       
  1115 			if top.endswith('.inf'):
       
  1116 				continue
       
  1117 			elif top == 'group':
       
  1118 				continue
       
  1119 			else:
       
  1120 				pathlist = [top]
       
  1121 				break
       
  1122 
       
  1123 		pathlist.append("c_"+uniqueid.hexdigest()[:16])
       
  1124 		return "/".join(pathlist)
       
  1125 
       
  1126 	def outputpath(self, platform):
       
  1127 		""" The full path where product from this bldinf is created."""
       
  1128 		return str(platform['SBS_BUILD_DIR']) + "/" + BldInfFile.outputPathFragment(self.filename)
       
  1129 
       
  1130 	def depspath(self, platform):
       
  1131 	   """ Where does dependency information go relative to platform's SBS_BUILD_DIR?
       
  1132 	       Subclasses should redefine this
       
  1133 	   """
       
  1134 	   return self.outputpath(platform) + "/bldinf." + platform['key_md5'] + ".d"
       
  1135 
       
  1136 
       
  1137 
       
  1138 class MMPRaptorBackend(MMPBackend):
       
  1139 	"""A parser "backend" for the MMP language
       
  1140 
       
  1141 	This is used to map recognised MMP syntax onto a buildspec """
       
  1142 
       
  1143 	# Support priorities, with case-fixed mappings for use
       
  1144 	epoc32priorities = {
       
  1145 		'low':'Low',
       
  1146 		'background':'Background',
       
  1147 		'foreground':'Foreground',
       
  1148 		'high':'High',
       
  1149 		'windowserver':'WindowServer',
       
  1150 		'fileserver':'FileServer',
       
  1151 		'realtimeserver':'RealTimeServer',
       
  1152 		'supervisor':'SuperVisor'
       
  1153 		}
       
  1154 
       
  1155 	# Known capability flags with associated bitwise operations
       
  1156 	supportedCapabilities = {
       
  1157 		'tcb':(1<<0),
       
  1158 		'commdd':(1<<1),
       
  1159 		'powermgmt':(1<<2),
       
  1160 		'multimediadd':(1<<3),
       
  1161 		'readdevicedata':(1<<4),
       
  1162 		'writedevicedata':(1<<5),
       
  1163 		'drm':(1<<6),
       
  1164 		'trustedui':(1<<7),
       
  1165 		'protserv':(1<<8),
       
  1166 		'diskadmin':(1<<9),
       
  1167 		'networkcontrol':(1<<10),
       
  1168 		'allfiles':(1<<11),
       
  1169 		'swevent':(1<<12),
       
  1170 		'networkservices':(1<<13),
       
  1171 		'localservices':(1<<14),
       
  1172 		'readuserdata':(1<<15),
       
  1173 		'writeuserdata':(1<<16),
       
  1174 		'location':(1<<17),
       
  1175 		'surroundingsdd':(1<<18),
       
  1176 		'userenvironment':(1<<19),
       
  1177 	# Old capability names have zero value
       
  1178 		'root':0,
       
  1179 		'mediadd':0,
       
  1180 		'readsystemdata':0,
       
  1181 		'writesystemdata':0,
       
  1182 		'sounddd':0,
       
  1183 		'uidd':0,
       
  1184 		'killanyprocess':0,
       
  1185 		'devman':0,
       
  1186 		'phonenetwork':0,
       
  1187 		'localnetwork':0
       
  1188 	  	}
       
  1189 
       
  1190 	library_re = re.compile(r"^(?P<name>[^{]+?)(?P<version>{(?P<major>[0-9]+)\.(?P<minor>[0-9]+)})?(\.(lib|dso))?$",re.I)
       
  1191 
       
  1192 
       
  1193 	def __init__(self, aRaptor, aMmpfilename, aBldInfFilename):
       
  1194 		super(MMPRaptorBackend,self).__init__()
       
  1195 		self.platformblock = None
       
  1196 		self.__Raptor = aRaptor
       
  1197 		self.BuildVariant = raptor_data.Variant()
       
  1198 		self.ResourceVariants = []
       
  1199 		self.BitmapVariants = []
       
  1200 		self.StringTableVariants = []
       
  1201 		self.__bldInfFilename = aBldInfFilename
       
  1202 		self.__targettype = "UNKNOWN"
       
  1203 		self.__currentMmpFile = aMmpfilename
       
  1204 		self.__defFileRoot = self.__currentMmpFile
       
  1205 		self.__currentLineNumber = 0
       
  1206 		self.__sourcepath = raptor_utilities.resolveSymbianPath(self.__currentMmpFile, "")
       
  1207 		self.__userinclude = ""
       
  1208 		self.__systeminclude = ""
       
  1209 		self.__bitmapSourcepath = self.__sourcepath
       
  1210 		self.__current_resource = ""
       
  1211 		self.__capabilities = []
       
  1212 		self.__resourceFiles = []
       
  1213 		self.__pageConflict = []
       
  1214 		self.__debuggable = ""
       
  1215 		self.sources = []
       
  1216 
       
  1217 		self.__TARGET = ""
       
  1218 		self.__TARGETEXT = ""
       
  1219 		self.deffile = ""
       
  1220 		self.__LINKAS = ""
       
  1221 		self.nostrictdef = False
       
  1222 		self.featureVariant = False
       
  1223 
       
  1224 		self.__currentResourceVariant = None
       
  1225 		self.__currentStringTableVariant = None
       
  1226 		self.__explicitversion = False
       
  1227 		self.__versionhex = ""
       
  1228 
       
  1229 		# "ALL" capability calculated based on the total capabilities currently supported
       
  1230 		allCapabilities = 0
       
  1231 		for supportedCapability in MMPRaptorBackend.supportedCapabilities.keys():
       
  1232 			allCapabilities = allCapabilities | MMPRaptorBackend.supportedCapabilities[supportedCapability]
       
  1233 		MMPRaptorBackend.supportedCapabilities['all'] = allCapabilities
       
  1234 
       
  1235 	# Permit unit-testing output without a Raptor context
       
  1236 	def __debug(self, format, *extras):
       
  1237 		if (self.__Raptor):
       
  1238 			self.__Raptor.Debug(format, *extras)
       
  1239 
       
  1240 	def __warn(self, format, *extras):
       
  1241 		if (self.__Raptor):
       
  1242 			self.__Raptor.Warn(format, *extras)
       
  1243 
       
  1244 	def doPreProcessorComment(self,s,loc,toks):
       
  1245 		commentDetail = getPreProcessorCommentDetail(toks[0])
       
  1246 		self.__currentMmpFile = commentDetail[0].GetLocalString()
       
  1247 		self.__currentLineNumber = commentDetail[1]
       
  1248 		self.__debug("Current file %s, line number %s\n"  % (self.__currentMmpFile,str(self.__currentLineNumber)))
       
  1249 		return "OK"
       
  1250 
       
  1251 	def doBlankLine(self,s,loc,toks):
       
  1252 		self.__currentLineNumber += 1
       
  1253 
       
  1254 	def doStartPlatform(self,s,loc,toks):
       
  1255 		self.__currentLineNumber += 1
       
  1256 		self.__debug( "Start Platform block "+toks[0])
       
  1257 		self.platformblock = toks[0]
       
  1258 		return "OK"
       
  1259 
       
  1260 	def doEndPlatform(self,s,loc,toks):
       
  1261 		self.__currentLineNumber += 1
       
  1262 		self.__debug( "Finalise platform " + self.platformblock)
       
  1263 		return "OK"
       
  1264 
       
  1265 	def doSetSwitch(self,s,loc,toks):
       
  1266 		self.__currentLineNumber += 1
       
  1267 		prefix=""
       
  1268 		varname = toks[0].upper()
       
  1269 
       
  1270 		# A bright spark made the optionname the same as
       
  1271 		# the env variable. One will override the other if we pass this
       
  1272 		# on to make.  Add a prefix to prevent the clash.
       
  1273 		if varname=='ARMINC':
       
  1274 			prefix="SET_"
       
  1275 			self.__debug( "Set switch "+toks[0]+" ON")
       
  1276 			self.BuildVariant.AddOperation(raptor_data.Set(prefix+varname, "1"))
       
  1277 
       
  1278 		elif varname=='NOSTRICTDEF':
       
  1279 			self.nostrictdef = True
       
  1280 			self.__debug( "Set switch "+toks[0]+" ON")
       
  1281 			self.BuildVariant.AddOperation(raptor_data.Set(prefix+varname, "1"))
       
  1282 
       
  1283 		elif varname == 'PAGED':
       
  1284 			self.BuildVariant.AddOperation(raptor_data.Set(varname, "1"))
       
  1285 			self.__debug( "Set switch PAGE ON")
       
  1286 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDCODE_OPTION", "paged"))
       
  1287 			self.__debug( "Set switch PAGEDCODE ON")
       
  1288 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDDATA_OPTION", "paged"))
       
  1289 			self.__debug( "Set data PAGEDDATA ON")
       
  1290 			self.__pageConflict.append("PAGEDCODE")
       
  1291 			self.__pageConflict.append("PAGEDDATA")
       
  1292 
       
  1293 		elif varname == 'UNPAGED':
       
  1294 			self.BuildVariant.AddOperation(raptor_data.Set("PAGED", "0"))
       
  1295 			self.__debug( "Set switch PAGED OFF")
       
  1296 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDCODE_OPTION", "unpaged"))
       
  1297 			self.__debug( "Set switch PAGEDCODE OFF")
       
  1298 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDDATA_OPTION", "unpaged"))
       
  1299 			self.__debug( "Set data PAGEDDATA OFF")
       
  1300 			self.__pageConflict.append("UNPAGEDCODE")
       
  1301 			self.__pageConflict.append("UNPAGEDDATA")
       
  1302 
       
  1303 		elif varname == 'PAGEDCODE':
       
  1304 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDCODE_OPTION", "paged"))
       
  1305 			self.__debug( "Set switch " + varname + " ON")
       
  1306 			self.__pageConflict.append(varname)
       
  1307 
       
  1308 		elif varname == 'PAGEDDATA':
       
  1309 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDDATA_OPTION", "paged"))
       
  1310 			self.__debug( "Set switch " + varname + " ON")
       
  1311 			self.__pageConflict.append(varname)
       
  1312 
       
  1313 		elif varname == 'UNPAGEDCODE':
       
  1314 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDCODE_OPTION", "unpaged"))
       
  1315 			self.__debug( "Set switch " + varname + " ON")
       
  1316 			self.__pageConflict.append(varname)
       
  1317 		elif varname == 'UNPAGEDDATA':
       
  1318 			self.BuildVariant.AddOperation(raptor_data.Set("PAGEDDATA_OPTION", "unpaged"))
       
  1319 			self.__debug( "Set switch " + varname + " ON")
       
  1320 			self.__pageConflict.append(varname)
       
  1321 
       
  1322 		elif varname == 'NOLINKTIMECODEGENERATION':
       
  1323 			self.BuildVariant.AddOperation(raptor_data.Set("LTCG",""))
       
  1324 			self.__debug( "Set switch " + varname + " OFF")
       
  1325 		elif varname == 'NOMULTIFILECOMPILATION':
       
  1326 			self.BuildVariant.AddOperation(raptor_data.Set("MULTIFILE_ENABLED",""))
       
  1327 			self.__debug( "Set switch " + varname + " OFF")
       
  1328 
       
  1329 		elif varname == 'DEBUGGABLE':
       
  1330 			if self.__debuggable != "udeb":
       
  1331 				self.__debuggable = "udeb urel"
       
  1332 			else:
       
  1333 				self.__Raptor.Warn("DEBUGGABLE keyword ignored as DEBUGGABLE_UDEBONLY is already specified")
       
  1334 		elif varname == 'DEBUGGABLE_UDEBONLY':
       
  1335 			if self.__debuggable != "":
       
  1336 				self.__Raptor.Warn("DEBUGGABLE keyword has no effect as DEBUGGABLE or DEBUGGABLE_UDEBONLY is already set")
       
  1337 			self.__debuggable = "udeb"
       
  1338 		elif varname == 'FEATUREVARIANT':
       
  1339 			self.BuildVariant.AddOperation(raptor_data.Set(varname,"1"))
       
  1340 			self.featureVariant = True
       
  1341 		else:
       
  1342 			self.__debug( "Set switch "+toks[0]+" ON")
       
  1343 			self.BuildVariant.AddOperation(raptor_data.Set(prefix+varname, "1"))
       
  1344 
       
  1345 		return "OK"
       
  1346 
       
  1347 	def doAssignment(self,s,loc,toks):
       
  1348 		self.__currentLineNumber += 1
       
  1349 		varname = toks[0].upper()
       
  1350 		if varname=='TARGET':
       
  1351 			(self.__TARGET, self.__TARGETEXT) = os.path.splitext(toks[1])
       
  1352 			self.__TARGETEXT = self.__TARGETEXT.lstrip('.')
       
  1353 
       
  1354 			self.BuildVariant.AddOperation(raptor_data.Set("REQUESTEDTARGETEXT", self.__TARGETEXT.lower()))
       
  1355 
       
  1356 			lowercase_TARGET = self.__TARGET.lower()
       
  1357 			self.__debug("Set "+toks[0]+" to " + lowercase_TARGET)
       
  1358 			self.__debug("Set REQUESTEDTARGETEXT to " + self.__TARGETEXT.lower())
       
  1359 
       
  1360 			self.BuildVariant.AddOperation(raptor_data.Set("TARGET", self.__TARGET))
       
  1361 			self.BuildVariant.AddOperation(raptor_data.Set("TARGET_lower", lowercase_TARGET))
       
  1362 			if  lowercase_TARGET !=  self.__TARGET:
       
  1363 				self.__debug("TARGET is not lowercase: '%s' - might cause BC problems." % self.__TARGET)
       
  1364 		elif varname=='TARGETTYPE':
       
  1365 			self.__debug("Set "+toks[0]+" to " + str(toks[1]))
       
  1366 			self.__targettype=toks[1]
       
  1367 			if  self.__targettype.lower() == "none":
       
  1368 				self.BuildVariant.AddOperation(raptor_data.Set("TARGET", ""))
       
  1369 				self.BuildVariant.AddOperation(raptor_data.Set("TARGET_lower",""))
       
  1370 				self.BuildVariant.AddOperation(raptor_data.Set("REQUESTEDTARGETEXT", ""))
       
  1371 			self.BuildVariant.AddOperation(raptor_data.Set(varname,toks[1].lower()))
       
  1372 
       
  1373 		elif varname=='TARGETPATH':
       
  1374 			value = toks[1].lower().replace('\\','/')
       
  1375 			self.__debug("Set "+varname+" to " + value)
       
  1376 			self.BuildVariant.AddOperation(raptor_data.Set(varname, value))
       
  1377 
       
  1378 		elif varname=='OPTION' or varname=='LINKEROPTION':
       
  1379 			self.__debug("Set "+toks[1]+varname+" to " + str(toks[2]))
       
  1380 			self.BuildVariant.AddOperation(raptor_data.Append(varname+"_"+toks[1].upper()," ".join(toks[2])))
       
  1381 
       
  1382 			# Warn about OPTION ARMASM
       
  1383 			if "armasm" in toks[1].lower():
       
  1384 				self.__Raptor.Warn(varname+" ARMASM has no effect (use OPTION ARMCC).")
       
  1385 
       
  1386 		elif varname=='OPTION_REPLACE':
       
  1387 			# Warn about OPTION_REPLACE ARMASM
       
  1388 			if "armasm" in toks[1].lower():
       
  1389 				self.__Raptor.Warn("OPTION_REPLACE ARMASM has no effect (use OPTION_REPLACE ARMCC).")
       
  1390 			else:
       
  1391 				args = " ".join(toks[2])
       
  1392 
       
  1393 				searchReplacePairs = self.resolveOptionReplace(args)
       
  1394 
       
  1395 				for searchReplacePair in searchReplacePairs:
       
  1396 					self.__debug("Append %s to OPTION_REPLACE_%s", searchReplacePair, toks[1].upper())
       
  1397 					self.BuildVariant.AddOperation(raptor_data.Append(varname+"_"+toks[1].upper(),searchReplacePair))
       
  1398 
       
  1399 		elif varname=='SYSTEMINCLUDE' or varname=='USERINCLUDE':
       
  1400 			for path in toks[1]:
       
  1401 				resolved = raptor_utilities.resolveSymbianPath(self.__currentMmpFile, path)
       
  1402 				self.BuildVariant.AddOperation(raptor_data.Append(varname,resolved))
       
  1403 
       
  1404 				if varname=='SYSTEMINCLUDE':
       
  1405 					self.__systeminclude += ' ' + resolved
       
  1406 					self.__debug("  %s = %s",varname, self.__systeminclude)
       
  1407 				else:
       
  1408 					self.__userinclude += ' ' + resolved
       
  1409 					self.__debug("  %s = %s",varname, self.__userinclude)
       
  1410 
       
  1411 				self.__debug("Appending %s to %s",resolved, varname)
       
  1412 
       
  1413 			self.__systeminclude = self.__systeminclude.strip()
       
  1414 			self.__systeminclude = self.__systeminclude.rstrip('\/')
       
  1415 			self.__userinclude = self.__userinclude.strip()
       
  1416 			self.__userinclude = self.__userinclude.rstrip('\/')
       
  1417 
       
  1418 		elif varname=='EXPORTLIBRARY':
       
  1419 			# Remove extension from the EXPORTLIBRARY name
       
  1420 			libName = toks[1].rsplit(".", 1)[0]
       
  1421 			self.__debug("Set "+varname+" to " + libName)
       
  1422 			self.BuildVariant.AddOperation(raptor_data.Set(varname,"".join(libName)))
       
  1423 
       
  1424 		elif varname=='CAPABILITY':
       
  1425 			for cap in toks[1]:
       
  1426 				self.BuildVariant.AddOperation(raptor_data.Append(varname,cap," "))
       
  1427 				self.__debug("Setting  "+toks[0]+": " + cap)
       
  1428 				self.__capabilities.append(cap.lower())
       
  1429 		elif varname=='DEFFILE':
       
  1430 			self.__defFileRoot = self.__currentMmpFile
       
  1431 			self.deffile = toks[1]
       
  1432 		elif varname=='LINKAS':
       
  1433 			self.__debug("Set "+toks[0]+"  OPTION to " + str(toks[1]))
       
  1434 			self.__LINKAS = toks[1]
       
  1435 			self.BuildVariant.AddOperation(raptor_data.Set(varname, toks[1]))
       
  1436 		elif varname=='SECUREID' or varname=='VENDORID':
       
  1437 			hexoutput = MMPRaptorBackend.canonicalUID(toks[1])
       
  1438 			self.__debug("Set "+toks[0]+"  OPTION to " + hexoutput)
       
  1439 			self.BuildVariant.AddOperation(raptor_data.Set(varname, hexoutput))
       
  1440 		elif varname=='VERSION':
       
  1441 			if toks[-1] == "EXPLICIT":
       
  1442 				self.__explicitversion = True
       
  1443 				self.BuildVariant.AddOperation(raptor_data.Set("EXPLICITVERSION", "1"))
       
  1444 
       
  1445 			vm = re.match(r'^(\d+)(\.(\d+))?$', toks[1])
       
  1446 			if vm is not None:
       
  1447 				version = vm.groups()
       
  1448 				# the major version number
       
  1449 				major = int(version[0],10)
       
  1450 
       
  1451 				# add in the minor number
       
  1452 				minor = 0
       
  1453 				if len(version) >  1:
       
  1454 					minor = int(version[2],10)
       
  1455 
       
  1456 				self.__versionhex = "%04x%04x" % (major, minor)
       
  1457 				self.BuildVariant.AddOperation(raptor_data.Set(varname, "%d.%d" %(major, minor)))
       
  1458 				self.BuildVariant.AddOperation(raptor_data.Set(varname+"HEX", self.__versionhex))
       
  1459 				self.__debug("Set "+toks[0]+"  OPTION to " + toks[1])
       
  1460 				self.__debug("Set "+toks[0]+"HEX OPTION to " + "%04x%04x" % (major,minor))
       
  1461 
       
  1462 			else:
       
  1463 				self.__Raptor.Warn("Invalid version supplied to VERSION (%s), using default value" % toks[1])
       
  1464 
       
  1465 		elif varname=='EPOCHEAPSIZE':
       
  1466 			# Standardise on sending hex numbers to the FLMS.
       
  1467 
       
  1468 			if toks[1].lower().startswith('0x'):
       
  1469 				min = long(toks[1],16)
       
  1470 			else:
       
  1471 				min = long(toks[1],10)
       
  1472 
       
  1473 			if toks[2].lower().startswith('0x'):
       
  1474 				max = long(toks[2],16)
       
  1475 			else:
       
  1476 				max = long(toks[2],10)
       
  1477 
       
  1478 			self.BuildVariant.AddOperation(raptor_data.Set(varname+"MIN", "%x" % min))
       
  1479 			self.__debug("Set "+varname+"MIN  OPTION to '%x' (hex)" % min )
       
  1480 			self.BuildVariant.AddOperation(raptor_data.Set(varname+"MAX", "%x" % max))
       
  1481 			self.__debug("Set "+varname+"MAX  OPTION to '%x' (hex)" % max )
       
  1482 
       
  1483 			# Some toolchains require decimal versions of the min/max values, converted to KB and
       
  1484 			# rounded up to the next 1KB boundary
       
  1485 			min_dec_kb = (int(min) + 1023) / 1024
       
  1486 			max_dec_kb = (int(max) + 1023) / 1024
       
  1487 			self.BuildVariant.AddOperation(raptor_data.Set(varname+"MIN_DEC_KB", "%d" % min_dec_kb))
       
  1488 			self.__debug("Set "+varname+"MIN  OPTION KB to '%d' (dec)" % min_dec_kb )
       
  1489 			self.BuildVariant.AddOperation(raptor_data.Set(varname+"MAX_DEC_KB", "%d" % max_dec_kb))
       
  1490 			self.__debug("Set "+varname+"MAX  OPTION KB to '%d' (dec)" % max_dec_kb )
       
  1491 
       
  1492 		elif varname=='EPOCSTACKSIZE':
       
  1493 			if toks[1].lower().startswith('0x'):
       
  1494 				stack = long(toks[1],16)
       
  1495 			else:
       
  1496 				stack = long(toks[1],10)
       
  1497 			self.BuildVariant.AddOperation(raptor_data.Set(varname, "%x" % stack))
       
  1498 			self.__debug("Set "+varname+"  OPTION to '%x' (hex)" % stack  )
       
  1499 		elif varname=='EPOCPROCESSPRIORITY':
       
  1500 			# low, background, foreground, high, windowserver, fileserver, realtimeserver or supervisor
       
  1501 			# These are case insensitive in metadata entries, but must be mapped to a static case pattern for use
       
  1502 			prio = toks[1].lower()
       
  1503 
       
  1504 			# NOTE: Original validation here didn't actually work.  This has been corrected to provide an error, but probably needs re-examination.
       
  1505 			if not MMPRaptorBackend.epoc32priorities.has_key(prio):
       
  1506 				self.__Raptor.Error("Priority setting '%s' is not a valid priority - should be one of %s.", prio, MMPRaptorBackend.epoc32priorities.values())
       
  1507 			else:
       
  1508 				self.__debug("Set "+toks[0]+" to " +  MMPRaptorBackend.epoc32priorities[prio])
       
  1509 				self.BuildVariant.AddOperation(raptor_data.Set(varname,MMPRaptorBackend.epoc32priorities[prio]))
       
  1510 		elif varname=='ROMTARGET' or varname=='RAMTARGET':
       
  1511 			if len(toks) == 1:
       
  1512 				self.__debug("Set "+toks[0]+" to <none>" )
       
  1513 				self.BuildVariant.AddOperation(raptor_data.Set(varname,"<none>"))
       
  1514 			else:
       
  1515 				toks1 = str(toks[1]).replace("\\","/")
       
  1516 				if toks1.find(","):
       
  1517 					toks1 = re.sub("[,'\[\]]", "", toks1).replace("//","/")
       
  1518 				self.__debug("Set "+toks[0]+" to " + toks1)
       
  1519 				self.BuildVariant.AddOperation(raptor_data.Set(varname,toks1))
       
  1520 
       
  1521 		else:
       
  1522 			self.__debug("Set "+toks[0]+" to " + str(toks[1]))
       
  1523 			self.BuildVariant.AddOperation(raptor_data.Set(varname,"".join(toks[1])))
       
  1524 
       
  1525 			if varname=='LINKAS':
       
  1526 				self.__LINKAS = toks[1]
       
  1527 
       
  1528 		return "OK"
       
  1529 
       
  1530 	def doAppend(self,s,loc,toks):
       
  1531 		self.__currentLineNumber += 1
       
  1532 		"""MMP command
       
  1533 		"""
       
  1534 		name=toks[0].upper()
       
  1535 		if len(toks) == 1:
       
  1536 			# list can be empty e.g. MACRO _FRED_ when fred it defined in the HRH
       
  1537 			# causes us to see just "MACRO" in the input - it is valid to ignore this
       
  1538 			self.__debug("Empty append list for " + name)
       
  1539 			return "OK"
       
  1540 		self.__debug("Append to "+name+" the values: " +str(toks[1]))
       
  1541 
       
  1542 		if name=='MACRO':
       
  1543 			name='MMPDEFS'
       
  1544 		elif name=='LANG':
       
  1545 			# don't break the environment variable
       
  1546 			name='LANGUAGES'
       
  1547 
       
  1548 		for item in toks[1]:
       
  1549 			if name=='MMPDEFS':
       
  1550 				# Unquote any macros since the FLM does it anyhow
       
  1551 				if item.startswith('"') and item.endswith('"') \
       
  1552 				or item.startswith("'") and item.endswith("'"):
       
  1553 					item = item.strip("'\"")
       
  1554 			if name=='LIBRARY' or name=='DEBUGLIBRARY':
       
  1555 				im = MMPRaptorBackend.library_re.match(item)
       
  1556 				if not im:
       
  1557 					self.__error("LIBRARY: %s Seems to have an invalid name.\nExpected xxxx.lib or xxxx.dso\n where xxxx might be\n\tname or \n\tname(n,m) where n is a major version number and m is a minor version number\n" %item)
       
  1558 				d = im.groupdict()
       
  1559 
       
  1560 				item = d['name']
       
  1561 				if d['version'] is not None:
       
  1562 					item += "{%04x%04x}" % (int(d['major']), int(d['minor']))
       
  1563 				item += ".dso"
       
  1564 			elif name=='STATICLIBRARY':
       
  1565 				# the FLM will decide on the ending appropriate to the platform
       
  1566 				item = re.sub(r"^(.*)\.[Ll][Ii][Bb]$",r"\1", item)
       
  1567 			elif name=="LANGUAGES":
       
  1568 				item = item.lower()
       
  1569 			elif (name=="WIN32_LIBRARY" and (item.startswith(".") or re.search(r'[\\|/]',item))) \
       
  1570 				or (name=="WIN32_RESOURCE"):
       
  1571 				# Relatively pathed win32 libraries, and all win32 resources, are resolved in relation
       
  1572 				# to the wrapper bld.inf file in which their .mmp file is specified.  This equates to
       
  1573 				# the current working directory in ABLD operation.
       
  1574 				item = raptor_utilities.resolveSymbianPath(self.__bldInfFilename, item)
       
  1575 				
       
  1576 			self.BuildVariant.AddOperation(raptor_data.Append(name,item," "))
       
  1577 			
       
  1578 			# maintain a debug library list, the same as LIBRARY but with DEBUGLIBRARY values
       
  1579 			# appended as they are encountered
       
  1580 			if name=='LIBRARY' or name=='DEBUGLIBRARY':
       
  1581 				self.BuildVariant.AddOperation(raptor_data.Append("LIBRARY_DEBUG",item," "))			
       
  1582 
       
  1583 		return "OK"
       
  1584 
       
  1585 	def canonicalUID(number):
       
  1586 		""" convert a UID string into an 8 digit hexadecimal string without leading 0x """
       
  1587 		if number.lower().startswith("0x"):
       
  1588 			n = int(number,16)
       
  1589 		else:
       
  1590 			n = int(number,10)
       
  1591 
       
  1592 		return "%08x" % n
       
  1593 
       
  1594 	canonicalUID = staticmethod(canonicalUID)
       
  1595 
       
  1596 	def doUIDAssignment(self,s,loc,toks):
       
  1597 		"""A single UID command results in a number of spec variables"""
       
  1598 		self.__currentLineNumber += 1
       
  1599 
       
  1600 		hexoutput = MMPRaptorBackend.canonicalUID(toks[1][0])
       
  1601 		self.__debug( "Set UID2 to %s" % hexoutput )
       
  1602 		self.BuildVariant.AddOperation(raptor_data.Set("UID2", hexoutput))
       
  1603 
       
  1604 		if len(toks[1]) > 1:
       
  1605 			hexoutput = MMPRaptorBackend.canonicalUID(toks[1][1])
       
  1606 			self.__debug( "Set UID3 to %s" % hexoutput)
       
  1607 			self.BuildVariant.AddOperation(raptor_data.Set("UID3", hexoutput))
       
  1608 
       
  1609 		self.__debug( "done set UID")
       
  1610 		return "OK"
       
  1611 
       
  1612 	def doSourcePathAssignment(self,s,loc,toks):
       
  1613 		self.__currentLineNumber += 1
       
  1614 		self.__sourcepath = raptor_utilities.resolveSymbianPath(self.__currentMmpFile, toks[1])
       
  1615 		self.__debug( "Remembering self.sourcepath state:  "+str(toks[0])+" is now " + self.__sourcepath)
       
  1616 		self.__debug("selfcurrentMmpFile: " + self.__currentMmpFile)
       
  1617 		return "OK"
       
  1618 
       
  1619 
       
  1620 	def doSourceAssignment(self,s,loc,toks):
       
  1621 		self.__currentLineNumber += 1
       
  1622 		self.__debug( "Setting "+toks[0]+" to " + str(toks[1]))
       
  1623 		for file in toks[1]:
       
  1624 			# file is always relative to sourcepath but some MMP files
       
  1625 			# have items that begin with a slash...
       
  1626 			file = file.lstrip("/")
       
  1627 			source = generic_path.Join(self.__sourcepath, file)
       
  1628 
       
  1629 			# If the SOURCEPATH itself begins with a '/', then dont look up the caseless version, since
       
  1630 			# we don't know at this time what $(EPOCROOT) will evaluate to.
       
  1631 			if source.GetLocalString().startswith('$(EPOCROOT)'):
       
  1632 				self.sources.append(str(source))	
       
  1633 				self.__debug("Append SOURCE " + str(source))
       
  1634 
       
  1635 			else:
       
  1636 				foundsource = source.FindCaseless()
       
  1637 				if foundsource == None:
       
  1638 					# Hope that the file will be generated later
       
  1639 					self.__debug("Sourcefile not found: %s" % source)
       
  1640 					foundsource = source
       
  1641 
       
  1642 				self.sources.append(str(foundsource))	
       
  1643 				self.__debug("Append SOURCE " + str(foundsource))
       
  1644 
       
  1645 
       
  1646 		self.__debug("		sourcepath: " + self.__sourcepath)
       
  1647 		return "OK"
       
  1648 
       
  1649 	# Resource
       
  1650 
       
  1651 	def doOldResourceAssignment(self,s,loc,toks):
       
  1652 		# Technically deprecated, but still used, so...
       
  1653 		self.__currentLineNumber += 1
       
  1654 		self.__debug("Processing old-style "+toks[0]+" "+str(toks[1]))
       
  1655 
       
  1656 		sysRes = (toks[0].lower() == "systemresource")
       
  1657 
       
  1658 		for rss in toks[1]:
       
  1659 			variant = raptor_data.Variant()
       
  1660 
       
  1661 			source = generic_path.Join(self.__sourcepath, rss)
       
  1662 			variant.AddOperation(raptor_data.Set("SOURCE", str(source)))
       
  1663 			self.__resourceFiles.append(str(source))
       
  1664 
       
  1665 			target = source.File().rsplit(".", 1)[0]	# remove the extension
       
  1666 			variant.AddOperation(raptor_data.Set("TARGET", target))
       
  1667 			variant.AddOperation(raptor_data.Set("TARGET_lower", target.lower()))
       
  1668 
       
  1669 			header = target.lower() + ".rsg"			# filename policy
       
  1670 			variant.AddOperation(raptor_data.Set("HEADER", header))
       
  1671 
       
  1672 			if sysRes:
       
  1673 				dsrtp = self.getDefaultSystemResourceTargetPath()
       
  1674 				variant.AddOperation(raptor_data.Set("TARGETPATH", dsrtp))
       
  1675 
       
  1676 			self.ResourceVariants.append(variant)
       
  1677 
       
  1678 		return "OK"
       
  1679 
       
  1680 	def getDefaultSystemResourceTargetPath(self):
       
  1681 		# the default systemresource TARGETPATH value should come from the
       
  1682 		# configuration rather than being hard-coded here. Then again, this
       
  1683 		# should really be deprecated away into oblivion...
       
  1684 		return "system/data"
       
  1685 
       
  1686 
       
  1687 	def getDefaultResourceTargetPath(self, targettype):
       
  1688 		# the different default TARGETPATH values should come from the
       
  1689 		# configuration rather than being hard-coded here.
       
  1690 		if targettype == "plugin":
       
  1691 			return "resource/plugins"
       
  1692 		if targettype == "pdl":
       
  1693 			return "resource/printers"
       
  1694 		return ""
       
  1695 
       
  1696 	def resolveOptionReplace(self, content):
       
  1697 		"""
       
  1698 		Constructs search/replace pairs based on .mmp OPTION_REPLACE entries for use on tool command lines
       
  1699 		within FLMS.
       
  1700 
       
  1701 		Depending on what's supplied to OPTION_REPLACE <TOOL>, the core part of the <TOOL> command line
       
  1702 		in the relevant FLM will have search and replace actions performed on it post-expansion (but pre-
       
  1703 		any OPTION <TOOL> additions).
       
  1704 
       
  1705 		In terms of logic, we try to follow what ABLD does, as the current behaviour is undocumented.
       
  1706 		What happens is a little inconsistent, and best described by some generic examples:
       
  1707 
       
  1708 			OPTION_REPLACE TOOL existing_option replacement_value
       
  1709 
       
  1710 				Replace all instances of "option existing_value" with "option replacement_value"
       
  1711 
       
  1712 			OPTION_REPLACE TOOL existing_option replacement_option
       
  1713 
       
  1714 				Replace all instances of "existing_option" with "replacement_option".
       
  1715 
       
  1716 			If "existing_option" is present in isolation then a removal is performed.
       
  1717 
       
  1718 		Any values encountered that don't follow an option are ignored.
       
  1719 		Options are identified as being prefixed with either '-' or '--'.
       
  1720 
       
  1721 		The front-end processes each OPTION_REPLACE entry and then appends one or more search/replace pairs
       
  1722 		to an OPTION_REPLACE_<TOOL> variable in the following format:
       
  1723 
       
  1724 		     search<->replace
       
  1725 		"""
       
  1726 		# Note that, for compatibility reasons, the following is mostly a port to Python of the corresponding
       
  1727 		# ABLD Perl, and hence maintains ABLD's idiosyncrasies in what it achieves
       
  1728 
       
  1729 		searchReplacePairs = []
       
  1730 		matches = re.findall("-{1,2}\S+\s*(?!-)\S*",content)
       
  1731 
       
  1732 		if matches:
       
  1733 			# reverse so we can process as a stack whilst retaining original order
       
  1734 			matches.reverse()
       
  1735 
       
  1736 			while (len(matches)):
       
  1737 				match = matches.pop()
       
  1738 
       
  1739 				standaloneMatch = re.match('^(?P<option>\S+)\s+(?P<value>\S+)$', match)
       
  1740 
       
  1741 				if (standaloneMatch):
       
  1742 					# Option listed standalone with a replacement value
       
  1743 					# Example:
       
  1744 					# 	OPTION_REPLACE ARMCC --cpu 6
       
  1745 					# Intention:
       
  1746 					# 	Replace instances of  "--cpu <something>" with "--cpu 6"
       
  1747 
       
  1748 					# Substitute any existing "option <existing_value>" instances with a single word
       
  1749 					# "@@<existing_value>" for later replacement
       
  1750 					searchReplacePairs.append('%s <->@@' % standaloneMatch.group('option'))
       
  1751 
       
  1752 					# Replace "@@<existing_value>" entries from above with "option <new_value>" entries
       
  1753 					# A pattern substitution is used to cover pre-existing values
       
  1754 					searchReplacePairs.append('@@%%<->%s %s' % (standaloneMatch.group('option'), standaloneMatch.group('value')))
       
  1755 				else:
       
  1756 					# Options specified in search/replace pairs with optional values
       
  1757 					# Example:
       
  1758 					#	OPTION_REPLACE ARMCC --O2 --O3
       
  1759 					# Intention:
       
  1760 					#	Replace instances of "--O2" with "--O3"
       
  1761 
       
  1762 					# At this point we will be looking at just the search option - there may or may not
       
  1763 					# be a replacement to consider
       
  1764 					search = match
       
  1765 					replace = ""
       
  1766 					if len(matches):
       
  1767 						replace = matches.pop()
       
  1768 					
       
  1769 					searchReplacePairs.append('%s<->%s' % (search, replace))
       
  1770 
       
  1771 			# Replace spaces to maintain word-based grouping in downstream makefile lists
       
  1772 			for i in range(0,len(searchReplacePairs)):
       
  1773 				searchReplacePairs[i] = searchReplacePairs[i].replace(' ','%20')
       
  1774 
       
  1775 		return searchReplacePairs
       
  1776 
       
  1777 	def doStartResource(self,s,loc,toks):
       
  1778 		self.__currentLineNumber += 1
       
  1779 		self.__debug("Start RESOURCE "+toks[1])
       
  1780 
       
  1781 		self.__current_resource = generic_path.Path(self.__sourcepath, toks[1])
       
  1782 		self.__current_resource = str(self.__current_resource)
       
  1783 
       
  1784 		self.__debug("sourcepath: " + self.__sourcepath)
       
  1785 		self.__debug("self.__current_resource source: " + toks[1])
       
  1786 		self.__debug("adjusted self.__current_resource source=" + self.__current_resource)
       
  1787 
       
  1788 		self.__currentResourceVariant = raptor_data.Variant()
       
  1789 		self.__currentResourceVariant.AddOperation(raptor_data.Set("SOURCE", self.__current_resource))
       
  1790 		self.__resourceFiles.append(self.__current_resource)
       
  1791 
       
  1792 		# The target name is the basename of the resource without the extension
       
  1793 		# e.g. "/fred/129ab34f.rss" would have a target name of "129ab34f"
       
  1794 		target = self.__current_resource.rsplit("/",1)[-1]
       
  1795 		target = target.rsplit(".",1)[0]
       
  1796 		self.__currentResourceVariant.AddOperation(raptor_data.Set("TARGET", target))
       
  1797 		self.__currentResourceVariant.AddOperation(raptor_data.Set("TARGET_lower", target.lower()))
       
  1798 		self.__headerspecified = False
       
  1799 		self.__headeronlyspecified = False
       
  1800 		self.__current_resource_header = target.lower() + ".rsg"
       
  1801 
       
  1802 		return "OK"
       
  1803 
       
  1804 	def doResourceAssignment(self,s,loc,toks):
       
  1805 		""" Assign variables for resource files """
       
  1806 		self.__currentLineNumber += 1
       
  1807 		varname = toks[0].upper() # the mmp keyword
       
  1808 		varvalue = "".join(toks[1])
       
  1809 
       
  1810 		# Get rid of any .rsc extension because the build system
       
  1811 		# needs to have it stripped off to calculate other names
       
  1812 		# for other purposes and # we aren't going to make it
       
  1813 		# optional anyhow.
       
  1814 		if varname == "TARGET":
       
  1815 			target_withext = varvalue.rsplit("/\\",1)[-1]
       
  1816 			target = target_withext.rsplit(".",1)[0]
       
  1817 			self.__current_resource_header = target.lower() + ".rsg"
       
  1818 			self.__currentResourceVariant.AddOperation(raptor_data.Set("TARGET_lower", target.lower()))
       
  1819 			self.__debug("Set resource "+varname+" to " + target)
       
  1820 			self.__currentResourceVariant.AddOperation(raptor_data.Set(varname,target))
       
  1821 		if varname == "TARGETPATH":
       
  1822 			varvalue=varvalue.replace('\\','/')
       
  1823 			self.__debug("Set resource "+varname+" to " + varvalue)
       
  1824 			self.__currentResourceVariant.AddOperation(raptor_data.Set(varname,varvalue))
       
  1825 		else:
       
  1826 			self.__debug("Set resource "+varname+" to " + varvalue)
       
  1827 			self.__currentResourceVariant.AddOperation(raptor_data.Set(varname,varvalue))
       
  1828 		return "OK"
       
  1829 
       
  1830 	def doResourceAppend(self,s,loc,toks):
       
  1831 		self.__currentLineNumber += 1
       
  1832 		self.__debug("Append resource to "+toks[0]+" the values: " +str(toks[1]))
       
  1833 		varname = toks[0].upper()
       
  1834 
       
  1835 		# we cannot use LANG as it interferes with the environment
       
  1836 		if varname == "LANG":
       
  1837 			varname = "LANGUAGES"
       
  1838 
       
  1839 		for item in toks[1]:
       
  1840 			if varname == "LANGUAGES":
       
  1841 				item = item.lower()
       
  1842 			self.__currentResourceVariant.AddOperation(raptor_data.Append(varname,item))
       
  1843 		return "OK"
       
  1844 
       
  1845 	def doResourceSetSwitch(self,s,loc,toks):
       
  1846 		self.__currentLineNumber += 1
       
  1847 		name = toks[0].upper()
       
  1848 
       
  1849 		if name == "HEADER":
       
  1850 			self.__headerspecified = True
       
  1851 
       
  1852 		elif name == "HEADERONLY":
       
  1853 			self.__headeronlyspecified = True
       
  1854 
       
  1855 		else:
       
  1856 			value = "1"
       
  1857 			self.__debug( "Set resource switch " + name + " " + value)
       
  1858 			self.__currentResourceVariant.AddOperation(raptor_data.Set(name, value))
       
  1859 
       
  1860 		return "OK"
       
  1861 
       
  1862 	def doEndResource(self,s,loc,toks):
       
  1863 		self.__currentLineNumber += 1
       
  1864 
       
  1865 		# Header name can change, depening if there was a TARGET defined or not, so it must be appended at the end
       
  1866 		if self.__headerspecified:
       
  1867 			self.__debug("Set resource switch HEADER " + self.__current_resource_header)
       
  1868 			self.__currentResourceVariant.AddOperation(raptor_data.Set("HEADER", self.__current_resource_header))
       
  1869 
       
  1870 		if self.__headeronlyspecified:
       
  1871 			self.__debug("Set resource switch HEADERONLY " + self.__current_resource_header)
       
  1872 			self.__currentResourceVariant.AddOperation(raptor_data.Set("HEADER", self.__current_resource_header))
       
  1873 			self.__currentResourceVariant.AddOperation(raptor_data.Set("HEADERONLY", "True"))
       
  1874 
       
  1875 		self.__debug("End RESOURCE")
       
  1876 		self.ResourceVariants.append(self.__currentResourceVariant)
       
  1877 		self.__currentResourceVariant = None
       
  1878 		self.__current_resource = ""
       
  1879 		return "OK"
       
  1880 
       
  1881 	# Bitmap
       
  1882 
       
  1883 	def doStartBitmap(self,s,loc,toks):
       
  1884 		self.__currentLineNumber += 1
       
  1885 		self.__debug("Start BITMAP "+toks[1])
       
  1886 
       
  1887 		self.__currentBitmapVariant = raptor_data.Variant(toks[1].replace('.','_'))
       
  1888 		# Use BMTARGET and BMTARGET_lower because that prevents
       
  1889 		# confusion with the TARGET and TARGET_lower of our parent MMP
       
  1890 		# when setting the OUTPUTPATH.  This in turn allows us to
       
  1891 		# not get tripped up by multiple mbms being generated with
       
  1892 		# the same name to the same directory.
       
  1893 		self.__currentBitmapVariant.AddOperation(raptor_data.Set("BMTARGET", toks[1]))
       
  1894 		self.__currentBitmapVariant.AddOperation(raptor_data.Set("BMTARGET_lower", toks[1].lower()))
       
  1895 		self.__currentBitmapVariant.AddOperation(raptor_data.Set("SOURCE", ""))
       
  1896 		return "OK"
       
  1897 
       
  1898 	def doBitmapAssignment(self,s,loc,toks):
       
  1899 		self.__currentLineNumber += 1
       
  1900 		self.__debug("Set bitmap "+toks[0]+" to " + str(toks[1]))
       
  1901 		name = toks[0].upper()
       
  1902 		value = "".join(toks[1])
       
  1903 		if name == "TARGETPATH":
       
  1904 			value = value.replace('\\','/')
       
  1905 
       
  1906 		self.__currentBitmapVariant.AddOperation(raptor_data.Set(name,value))
       
  1907 		return "OK"
       
  1908 
       
  1909 	def doBitmapSourcePathAssignment(self,s,loc,toks):
       
  1910 		self.__currentLineNumber += 1
       
  1911 		self.__debug("Previous bitmap sourcepath:" + self.__bitmapSourcepath)
       
  1912 		self.__bitmapSourcepath = raptor_utilities.resolveSymbianPath(self.__currentMmpFile, toks[1])
       
  1913 		self.__debug("New bitmap sourcepath: " + self.__bitmapSourcepath)
       
  1914 
       
  1915 	def doBitmapSourceAssignment(self,s,loc,toks):
       
  1916 		self.__currentLineNumber += 1
       
  1917 		self.__debug( "Setting "+toks[0]+" to " + str(toks[1]))
       
  1918 		# The first "source" is the colour depth for all the others.
       
  1919 		# The depth format is b[,m] where b is the bitmap depth and m is
       
  1920 		# the mask depth.
       
  1921 		# Valid values for b are: 1 2 4 8 c4 c8 c12 c16 c24 c32 c32a (?)
       
  1922 		# Valid values for m are: 1 8 (any number?)
       
  1923 		#
       
  1924 		# If m is specified then the bitmaps are in pairs: b0 m0 b1 m1...
       
  1925 		# If m is not specified then there are no masks, just bitmaps: b0 b1...
       
  1926 		colordepth = toks[1][0].lower()
       
  1927 		if "," in colordepth:
       
  1928 			(bitmapdepth, maskdepth) = colordepth.split(",")
       
  1929 		else:
       
  1930 			bitmapdepth = colordepth
       
  1931 			maskdepth = 0
       
  1932 
       
  1933 		sources=""
       
  1934 		mask = False
       
  1935 		for file in toks[1][1:]:
       
  1936 			path = generic_path.Join(self.__bitmapSourcepath, file)
       
  1937 			if sources:
       
  1938 				sources += " "
       
  1939 			if mask:
       
  1940 				sources += "DEPTH=" + maskdepth + " FILE=" + str(path)
       
  1941 			else:
       
  1942 				sources += "DEPTH=" + bitmapdepth + " FILE=" + str(path)
       
  1943 			if maskdepth:
       
  1944 				mask = not mask
       
  1945 		self.__debug("sources: " + sources)
       
  1946 		self.__currentBitmapVariant.AddOperation(raptor_data.Append("SOURCE", sources))
       
  1947 		return "OK"
       
  1948 
       
  1949 	def doBitmapSetSwitch(self,s,loc,toks):
       
  1950 		self.__currentLineNumber += 1
       
  1951 		self.__debug( "Set bitmap switch "+toks[0]+" ON")
       
  1952 		self.__currentBitmapVariant.AddOperation(raptor_data.Set(toks[0].upper(), "1"))
       
  1953 		return "OK"
       
  1954 
       
  1955 	def doEndBitmap(self,s,loc,toks):
       
  1956 		self.__currentLineNumber += 1
       
  1957 		self.__bitmapSourcepath = self.__sourcepath
       
  1958 		self.BitmapVariants.append(self.__currentBitmapVariant)
       
  1959 		self.__currentBitmapVariant = None
       
  1960 		self.__debug("End BITMAP")
       
  1961 		return "OK"
       
  1962 
       
  1963 	# Stringtable
       
  1964 
       
  1965 	def doStartStringTable(self,s,loc,toks):
       
  1966 		self.__currentLineNumber += 1
       
  1967 		self.__debug( "Start STRINGTABLE "+toks[1])
       
  1968 
       
  1969 		specstringtable = generic_path.Join(self.__sourcepath, toks[1])
       
  1970 		uniqname = specstringtable.File().replace('.','_') # corrected, filename only
       
  1971 		source = str(specstringtable.FindCaseless())
       
  1972 
       
  1973 		self.__debug("sourcepath: " + self.__sourcepath)
       
  1974 		self.__debug("stringtable: " + toks[1])
       
  1975 		self.__debug("adjusted stringtable source=" + source)
       
  1976 
       
  1977 		self.__currentStringTableVariant = raptor_data.Variant(uniqname)
       
  1978 		self.__currentStringTableVariant.AddOperation(raptor_data.Set("SOURCE", source))
       
  1979 		self.__currentStringTableVariant.AddOperation(raptor_data.Set("EXPORTPATH", ""))
       
  1980 		self.__stringtableExported = False
       
  1981 
       
  1982 		# The target name by default is the name of the stringtable without the extension
       
  1983 		# e.g. the stringtable "/fred/http.st" would have a default target name of "http"
       
  1984 		stringtable_withext = specstringtable.File()
       
  1985 		self.__stringtable = stringtable_withext.rsplit(".",1)[0].lower()
       
  1986 		self.__currentStringTableVariant.AddOperation(raptor_data.Set("TARGET", self.__stringtable))
       
  1987 
       
  1988 		self.__stringtableHeaderonlyspecified = False
       
  1989 
       
  1990 		return "OK"
       
  1991 
       
  1992 	def doStringTableAssignment(self,s,loc,toks):
       
  1993 		""" Assign variables for stringtables """
       
  1994 		self.__currentLineNumber += 1
       
  1995 		varname = toks[0].upper() # the mmp keyword
       
  1996 		varvalue = "".join(toks[1])
       
  1997 
       
  1998 		# Get rid of any .rsc extension because the build system
       
  1999 		# needs to have it stripped off to calculate other names
       
  2000 		# for other purposes and # we aren't going to make it
       
  2001 		# optional anyhow.
       
  2002 		if varname == "EXPORTPATH":
       
  2003 			finalvalue = raptor_utilities.resolveSymbianPath(self.__currentMmpFile, varvalue)
       
  2004 			self.__stringtableExported = True
       
  2005 		else:
       
  2006 			finalvalue = varvalue
       
  2007 
       
  2008 		self.__debug("Set stringtable "+varname+" to " + finalvalue)
       
  2009 		self.__currentStringTableVariant.AddOperation(raptor_data.Set(varname,finalvalue))
       
  2010 		return "OK"
       
  2011 
       
  2012 	def doStringTableSetSwitch(self,s,loc,toks):
       
  2013 		self.__currentLineNumber += 1
       
  2014 		if toks[0].upper()== "HEADERONLY":
       
  2015 			self.__stringtableHeaderonlyspecified = True
       
  2016 			self.__debug( "Set stringtable switch "+toks[0]+" ON")
       
  2017 			self.__currentStringTableVariant.AddOperation(raptor_data.Set(toks[0].upper(), "1"))
       
  2018 		return "OK"
       
  2019 
       
  2020 	def doEndStringTable(self,s,loc,toks):
       
  2021 		self.__currentLineNumber += 1
       
  2022 
       
  2023 		if not self.__stringtableExported:
       
  2024 			# There was no EXPORTPATH specified for this stringtable
       
  2025 			# so for our other code to be able to reference it we
       
  2026 			# must add the path of the generated location to the userinclude path
       
  2027 
       
  2028 			ipath = "$(OUTPUTPATH)"
       
  2029 			self.BuildVariant.AddOperation(raptor_data.Append("USERINCLUDE",ipath))
       
  2030 			self.__userinclude += ' ' + ipath
       
  2031 			self.__debug("  USERINCLUDE = %s", self.__userinclude)
       
  2032 			self.__userinclude.strip()
       
  2033 
       
  2034 		self.StringTableVariants.append(self.__currentStringTableVariant)
       
  2035 		self.__currentStringTableVariant = None
       
  2036 		self.__debug("End STRINGTABLE")
       
  2037 		if not self.__stringtableHeaderonlyspecified:
       
  2038 			# Have to assume that this is where the cpp file will be.  This has to be maintained
       
  2039 			# in sync with the FLM's idea of where this file should be.  We need a better way.
       
  2040 			# Interfaces also need outputs that allow other interfaces to refer to their outputs
       
  2041 			# without having to "know" where they will be.
       
  2042 			self.sources.append('$(OUTPUTPATH)/' + self.__stringtable + '.cpp')
       
  2043 		return "OK"
       
  2044 
       
  2045 
       
  2046 	def doUnknownStatement(self,s,loc,toks):
       
  2047 		self.__warn("%s (%d) : Unrecognised Keyword %s", self.__currentMmpFile, self.__currentLineNumber, str(toks))
       
  2048 		self.__currentLineNumber += 1
       
  2049 		return "OK"
       
  2050 
       
  2051 
       
  2052 	def doUnknownBlock(self,s,loc,toks):
       
  2053 		self.__warn("%s (%d) : Unrecognised Block %s", self.__currentMmpFile, self.__currentLineNumber, str(toks))
       
  2054 		self.__currentLineNumber += 1
       
  2055 		return "OK"
       
  2056 
       
  2057 	def doDeprecated(self,s,loc,toks):
       
  2058 		self.__debug( "Deprecated command " + str(toks))
       
  2059 		self.__warn("%s (%d) : %s is deprecated .mmp file syntax", self.__currentMmpFile, self.__currentLineNumber, str(toks))
       
  2060 		self.__currentLineNumber += 1
       
  2061 		return "OK"
       
  2062 
       
  2063 	def doNothing(self):
       
  2064 		self.__currentLineNumber += 1
       
  2065 		return "OK"
       
  2066 
       
  2067 	def finalise(self, aBuildPlatform):
       
  2068 		"""Post-processing of data that is only applicable in the context of a fully
       
  2069 		processed .mmp file."""
       
  2070 		resolvedDefFile = ""
       
  2071 
       
  2072 		if self.__TARGET:
       
  2073 			defaultRootName = self.__TARGET
       
  2074 			if self.__TARGETEXT!="":
       
  2075 				defaultRootName += "." + self.__TARGETEXT
       
  2076 
       
  2077 			# NOTE: Changing default .def file name based on the LINKAS argument is actually
       
  2078 			# a defect, but this follows the behaviour of the current build system.
       
  2079 			if (self.__LINKAS):
       
  2080 				defaultRootName = self.__LINKAS
       
  2081 
       
  2082 			resolvedDefFile = self.resolveDefFile(defaultRootName, aBuildPlatform)
       
  2083 			self.__debug("Resolved def file:  %s" % resolvedDefFile )
       
  2084 			# We need to store this resolved deffile location for the FREEZE target
       
  2085 			self.BuildVariant.AddOperation(raptor_data.Set("RESOLVED_DEFFILE", resolvedDefFile))
       
  2086 
       
  2087 		# If a deffile is specified, an FLM will put in a dependency.
       
  2088 		# If a deffile is specified then raptor_meta will guess a name but:
       
  2089 		#	1) If the guess is wrong then the FLM will complain "no rule to make ..."
       
  2090 		#	2) In some cases, e.g. plugin, 1) is not desirable as the presence of a def file
       
  2091 		#		is not a necessity.  In these cases the FLM needs to know if DEFFILE
       
  2092 		#		is a guess or not so it can decide if a dependency should be added.
       
  2093 
       
  2094 		# We check that the def file exists and that it is non-zero (incredible
       
  2095 		# that this should be needed).
       
  2096 
       
  2097 		deffile_keyword="1"
       
  2098 		if self.deffile == "":
       
  2099 			# If the user didn't specify a deffile name then
       
  2100 			# we must be guessing
       
  2101 			# Let's check if our guess actually corresponds to a
       
  2102 			# real file.  If it does then that confims the guess.
       
  2103 			#  If there's no file then we still need to pass make the name
       
  2104 			# so it can complain about there not being a DEF file
       
  2105 			# for this particular target type and fail to build this target.
       
  2106 
       
  2107 			deffile_keyword=""
       
  2108 			try:
       
  2109 				findpath = generic_path.Path(resolvedDefFile)
       
  2110 				foundfile = findpath.FindCaseless()
       
  2111 
       
  2112 				if foundfile == None:
       
  2113 					raise IOError("file not found")
       
  2114 
       
  2115 				self.__debug("Found DEFFILE  " + foundfile.GetLocalString())
       
  2116 				rfstat = os.stat(foundfile.GetLocalString())
       
  2117 
       
  2118 				mode = rfstat[stat.ST_MODE]
       
  2119 				if mode != None and stat.S_ISREG(mode) and rfstat[stat.ST_SIZE] > 0:
       
  2120 					resolvedDefFile = str(foundfile)
       
  2121 				else:
       
  2122 					resolvedDefFile=""
       
  2123 			except Exception,e:
       
  2124 				self.__debug("While Searching for an IMPLIED  DEFFILE: %s: %s" % (str(e),str(findpath)) )
       
  2125 				resolvedDefFile=""
       
  2126 		else:
       
  2127 			if not resolvedDefFile == "":
       
  2128 				try:
       
  2129 					findpath = generic_path.Path(resolvedDefFile)
       
  2130 					resolvedDefFile = str(findpath.FindCaseless())
       
  2131 					if resolvedDefFile=="None":
       
  2132 						raise IOError("file not found")
       
  2133 				except Exception,e:
       
  2134 					self.__warn("While Searching for a SPECIFIED DEFFILE: %s: %s" % (str(e),str(findpath)) )
       
  2135 					resolvedDefFile=""
       
  2136 			else:
       
  2137 				self.__warn("DEFFILE KEYWORD used (%s) but def file not resolved" % (self.deffile) )
       
  2138 
       
  2139 
       
  2140 		self.BuildVariant.AddOperation(raptor_data.Set("DEFFILE", resolvedDefFile))
       
  2141 		self.__debug("Set DEFFILE to " + resolvedDefFile)
       
  2142 		self.BuildVariant.AddOperation(raptor_data.Set("DEFFILEKEYWORD", deffile_keyword))
       
  2143 		self.__debug("Set DEFFILEKEYWORD to '%s'",deffile_keyword)
       
  2144 
       
  2145 		# if this target type has a default TARGETPATH other than "" for
       
  2146 		# resources then we need to add that default to all resources which
       
  2147 		# do not explicitly set the TARGETPATH themselves.
       
  2148 		tp = self.getDefaultResourceTargetPath(self.getTargetType())
       
  2149 		if tp:
       
  2150 			for i,var in enumerate(self.ResourceVariants):
       
  2151 				# does this resource specify its own TARGETPATH?
       
  2152 				needTP = True
       
  2153 				for op in var.ops:
       
  2154 					if isinstance(op, raptor_data.Set) \
       
  2155 					and op.name == "TARGETPATH":
       
  2156 						needTP = False
       
  2157 						break
       
  2158 				if needTP:
       
  2159 					self.ResourceVariants[i].AddOperation(raptor_data.Set("TARGETPATH", tp))
       
  2160 
       
  2161 		# some core build configurations need to know about the resource builds, and
       
  2162 		# some resource building configurations need knowledge of the core build
       
  2163 		for resourceFile in self.__resourceFiles:
       
  2164 			self.BuildVariant.AddOperation(raptor_data.Append("RESOURCEFILES", resourceFile))
       
  2165 
       
  2166 		for i,var in enumerate(self.ResourceVariants):
       
  2167 			self.ResourceVariants[i].AddOperation(raptor_data.Set("MAIN_TARGET_lower", self.__TARGET.lower()))
       
  2168 			self.ResourceVariants[i].AddOperation(raptor_data.Set("MAIN_REQUESTEDTARGETEXT", self.__TARGETEXT.lower()))
       
  2169 
       
  2170 		# Resolve combined capabilities as hex flags, for configurations that require them
       
  2171 		capabilityFlag1 = 0
       
  2172 		capabilityFlag2 = 0			# Always 0
       
  2173 
       
  2174 		for capability in self.__capabilities:
       
  2175 			invert = 0
       
  2176 
       
  2177 			if capability.startswith('-'):
       
  2178 				invert = 0xffffffff
       
  2179 				capability = capability.lstrip('-')
       
  2180 
       
  2181 			if MMPRaptorBackend.supportedCapabilities.has_key(capability):
       
  2182 				capabilityFlag1 = capabilityFlag1 ^ invert
       
  2183 				capabilityFlag1 = capabilityFlag1 | MMPRaptorBackend.supportedCapabilities[capability]
       
  2184 				capabilityFlag1 = capabilityFlag1 ^ invert
       
  2185 
       
  2186 		capabilityFlag1 = "%08xu" % capabilityFlag1
       
  2187 		capabilityFlag2 = "%08xu" % capabilityFlag2
       
  2188 
       
  2189 		self.BuildVariant.AddOperation(raptor_data.Set("CAPABILITYFLAG1", capabilityFlag1))
       
  2190 		self.__debug ("Set CAPABILITYFLAG1 to " + capabilityFlag1)
       
  2191 		self.BuildVariant.AddOperation(raptor_data.Set("CAPABILITYFLAG2", capabilityFlag2))
       
  2192 		self.__debug ("Set CAPABILITYFLAG2 to " + capabilityFlag2)
       
  2193 
       
  2194 		# For non-Feature Variant builds, the location of the product include hrh file is
       
  2195 		# appended to the SYSTEMINCLUDE list
       
  2196 		if not aBuildPlatform['ISFEATUREVARIANT']:
       
  2197 			productIncludePath = str(aBuildPlatform['VARIANT_HRH'].Dir())
       
  2198 			self.BuildVariant.AddOperation(raptor_data.Append("SYSTEMINCLUDE",productIncludePath))
       
  2199 			self.__debug("Appending product include location %s to SYSTEMINCLUDE",productIncludePath)
       
  2200 
       
  2201 		# Specifying both a PAGED* and its opposite UNPAGED* keyword in a .mmp file
       
  2202 		# will generate a warning and the last keyword specified will take effect.
       
  2203 		self.__pageConflict.reverse()
       
  2204 		if "PAGEDCODE" in self.__pageConflict and "UNPAGEDCODE" in self.__pageConflict:
       
  2205 			for x in self.__pageConflict:
       
  2206 				if x == "PAGEDCODE" or x == "UNPAGEDCODE":
       
  2207 					self.__Raptor.Warn("Both PAGEDCODE and UNPAGEDCODE are specified. The last one %s will take effect" % x)
       
  2208 					break
       
  2209 		if "PAGEDDATA" in self.__pageConflict and "UNPAGEDDATA" in self.__pageConflict:
       
  2210 			for x in self.__pageConflict:
       
  2211 				if x == "PAGEDDATA" or x == "UNPAGEDDATA":
       
  2212 					self.__Raptor.Warn("Both PAGEDDATA and UNPAGEDDATA are specified. The last one %s will take effect" % x)
       
  2213 					break
       
  2214 
       
  2215 		# Set Debuggable
       
  2216 		self.BuildVariant.AddOperation(raptor_data.Set("DEBUGGABLE", self.__debuggable))
       
  2217 
       
  2218 		if self.__explicitversion:
       
  2219 			self.BuildVariant.AddOperation(raptor_data.Append("UNIQUETARGETPATH","$(TARGET_lower)_$(VERSIONHEX)_$(REQUESTEDTARGETEXT)",'/'))
       
  2220 		else:
       
  2221 			self.BuildVariant.AddOperation(raptor_data.Append("UNIQUETARGETPATH","$(TARGET_lower)_$(REQUESTEDTARGETEXT)",'/'))
       
  2222 
       
  2223 		# Put the list of sourcefiles in with one Set operation - saves memory
       
  2224 		# and performance over using multiple Append operations.
       
  2225 		self.BuildVariant.AddOperation(raptor_data.Set("SOURCE",
       
  2226 						   " ".join(self.sources)))
       
  2227 
       
  2228 	def getTargetType(self):
       
  2229 		"""Target type in lower case - the standard format"""
       
  2230 		return self.__targettype.lower()
       
  2231 
       
  2232 	def resolveDefFile(self, aTARGET, aBuildPlatform):
       
  2233 		"""Returns a fully resolved DEFFILE entry depending on .mmp file location and TARGET, DEFFILE and NOSTRICTDEF
       
  2234 		entries in the .mmp file itself (where appropriate).
       
  2235 		Is able to deal with target names that have multiple '.' characters e.g. messageintercept.esockdebug.dll
       
  2236 		"""
       
  2237 
       
  2238 		resolvedDefFile = ""
       
  2239 		platform = aBuildPlatform['PLATFORM']
       
  2240 
       
  2241 		# Not having a default .def file directory is a pretty strong indicator that
       
  2242 		# .def files aren't supported for the particular platform
       
  2243 		if PlatformDefaultDefFileDir.has_key(platform):
       
  2244 			(targetname,targetext) = os.path.splitext(aTARGET)
       
  2245 			(defname,defext) = os.path.splitext(self.deffile)
       
  2246 			if defext=="":
       
  2247 				defext = ".def"
       
  2248 
       
  2249 			# NOTE: WORKAROUND
       
  2250 			if len(targetext) > 4:
       
  2251 				targetname += defext
       
  2252 
       
  2253 			if not self.deffile:
       
  2254 				resolvedDefFile = targetname
       
  2255 			else:
       
  2256 				if re.search('[\\|\/]$', self.deffile):
       
  2257 					# If DEFFILE is *solely* a path, signified by ending in a slash, then TARGET is the
       
  2258 					# basis for the default .def filename but with the specified path as prefix
       
  2259 					resolvedDefFile = self.deffile + targetname
       
  2260 
       
  2261 				else:
       
  2262 					resolvedDefFile = defname
       
  2263 
       
  2264 				resolvedDefFile = resolvedDefFile.replace('~', PlatformDefaultDefFileDir[platform])
       
  2265 
       
  2266 			if resolvedDefFile:
       
  2267 				if not self.nostrictdef:
       
  2268 					resolvedDefFile += 'u'
       
  2269 
       
  2270 				if self.__explicitversion:
       
  2271 					resolvedDefFile += '{' + self.__versionhex + '}'
       
  2272 
       
  2273 				resolvedDefFile += defext
       
  2274 
       
  2275 
       
  2276 				# If a DEFFILE statement doesn't specify a path in any shape or form, prepend the default .def file
       
  2277 				# location based on the platform being built
       
  2278 				if not re.search('[\\\/]+', self.deffile):
       
  2279 					resolvedDefFile = '../'+PlatformDefaultDefFileDir[platform]+'/'+resolvedDefFile
       
  2280 
       
  2281 				resolvedDefFile = raptor_utilities.resolveSymbianPath(self.__defFileRoot, resolvedDefFile, 'DEFFILE', "", str(aBuildPlatform['EPOCROOT']))
       
  2282 
       
  2283 		return resolvedDefFile
       
  2284 
       
  2285 
       
  2286 class MetaReader(object):
       
  2287 	"""Entry point class for Symbian metadata processing.
       
  2288 
       
  2289 	Provides a means of integrating "traditional" Symbian metadata processing
       
  2290 	with the new Raptor build system."""
       
  2291 
       
  2292 	filesplit_re = re.compile(r"^(?P<name>.*)\.(?P<ext>[^\.]*)$")
       
  2293 
       
  2294 	def __init__(self, aRaptor, configsToBuild):
       
  2295 		self.__Raptor = aRaptor
       
  2296 		self.BuildPlatforms = []
       
  2297 		self.ExportPlatforms = []
       
  2298 
       
  2299 		# Get the version of CPP that we are using
       
  2300 		metadata = self.__Raptor.cache.FindNamedVariant("meta")
       
  2301 		evaluator = self.__Raptor.GetEvaluator(None, raptor_data.BuildUnit(metadata.name, [metadata]) )
       
  2302 		self.__gnucpp = self.CheckValue(evaluator, "GNUCPP")
       
  2303 		self.__defaultplatforms = self.CheckValue(evaluator, "DEFAULT_PLATFORMS")
       
  2304 		self.__basedefaultplatforms = self.CheckValue(evaluator, "BASE_DEFAULT_PLATFORMS")
       
  2305 		self.__baseuserdefaultplatforms = self.CheckValue(evaluator, "BASE_USER_DEFAULT_PLATFORMS")
       
  2306 
       
  2307 		# Only read each variant.cfg once
       
  2308 		variantCfgs = {}
       
  2309 
       
  2310 		# Group the list of configurations into "build platforms".
       
  2311 		# A build platform is a set of configurations which share
       
  2312 		# the same metadata. In other words, a set of configurations
       
  2313 		# for which the bld.inf and MMP files pre-process to exactly
       
  2314 		# the same text.
       
  2315 		platforms = {}
       
  2316 
       
  2317 		# Exports are not "platform dependent" but they are configuration
       
  2318 		# dependent because different configs can have different EPOCROOT
       
  2319 		# and VARIANT_HRH values. Each "build platform" has one associated
       
  2320 		# "export platform" but several "build platforms" can be associated
       
  2321 		# with the same "export platform".
       
  2322 		exports = {}
       
  2323 
       
  2324 		for buildConfig in configsToBuild:
       
  2325 			# get everything we need to know about the configuration
       
  2326 			evaluator = self.__Raptor.GetEvaluator(None, buildConfig)
       
  2327 
       
  2328 			detail = {}
       
  2329 			detail['PLATFORM'] = self.CheckValue(evaluator, "TRADITIONAL_PLATFORM")
       
  2330 			epocroot = self.CheckValue(evaluator, "EPOCROOT")
       
  2331 			detail['EPOCROOT'] = generic_path.Path(epocroot)
       
  2332 
       
  2333 			sbs_build_dir = self.CheckValue(evaluator, "SBS_BUILD_DIR")
       
  2334 			detail['SBS_BUILD_DIR'] = generic_path.Path(sbs_build_dir)
       
  2335 			flm_export_dir = self.CheckValue(evaluator, "FLM_EXPORT_DIR")
       
  2336 			detail['FLM_EXPORT_DIR'] = generic_path.Path(flm_export_dir)
       
  2337 			detail['CACHEID'] = flm_export_dir
       
  2338 			if raptor_utilities.getOSPlatform().startswith("win"):
       
  2339 				detail['PLATMACROS'] = self.CheckValue(evaluator,"PLATMACROS.WINDOWS")
       
  2340 			else:
       
  2341 				detail['PLATMACROS'] = self.CheckValue(evaluator,"PLATMACROS.LINUX")
       
  2342 
       
  2343 			# Apply OS variant provided we are not ignoring this
       
  2344 			if not self.__Raptor.ignoreOsDetection:
       
  2345 				self.__Raptor.Debug("Automatic OS detection enabled.")
       
  2346 				self.ApplyOSVariant(buildConfig, epocroot)
       
  2347 			else: # We are ignore OS versions so no detection required, so no variant will be applied
       
  2348 				self.__Raptor.Debug("Automatic OS detection disabled.")
       
  2349 
       
  2350 			# is this a feature variant config or an ordinary variant
       
  2351 			fv = evaluator.Get("FEATUREVARIANTNAME")
       
  2352 			if fv:
       
  2353 				variantHdr = self.CheckValue(evaluator, "VARIANT_HRH")
       
  2354 				variantHRH = generic_path.Path(variantHdr)
       
  2355 				detail['ISFEATUREVARIANT'] = True
       
  2356 			else:
       
  2357 				variantCfg = self.CheckValue(evaluator, "VARIANT_CFG")
       
  2358 				variantCfg = generic_path.Path(variantCfg)
       
  2359 				if not variantCfg in variantCfgs:
       
  2360 					# get VARIANT_HRH from the variant.cfg file
       
  2361 					varCfg = getVariantCfgDetail(detail['EPOCROOT'], variantCfg)
       
  2362 					variantCfgs[variantCfg] = varCfg['VARIANT_HRH']
       
  2363 					# we expect to always build ABIv2
       
  2364 					if not 'ENABLE_ABIV2_MODE' in varCfg:
       
  2365 						self.__Raptor.Warn("missing flag ENABLE_ABIV2_MODE in %s file. ABIV1 builds are not supported.",
       
  2366 										   str(variantCfg))
       
  2367 				variantHRH = variantCfgs[variantCfg]
       
  2368 				detail['ISFEATUREVARIANT'] = False
       
  2369 
       
  2370 			detail['VARIANT_HRH'] = variantHRH
       
  2371 			self.__Raptor.Info("'%s' uses variant hrh file '%s'", buildConfig.name, variantHRH)
       
  2372 			detail['SYSTEMINCLUDE'] = self.CheckValue(evaluator, "SYSTEMINCLUDE")
       
  2373 
       
  2374 			detail['METADEPS'] = [] # Dependency targets for all metadata files in this platform
       
  2375 
       
  2376 			# find all the interface names we need
       
  2377 			ifaceTypes = self.CheckValue(evaluator, "INTERFACE_TYPES")
       
  2378 			interfaces = ifaceTypes.split()
       
  2379 
       
  2380 			for iface in interfaces:
       
  2381 				detail[iface] = self.CheckValue(evaluator, "INTERFACE." + iface)
       
  2382 
       
  2383 			# not test code unless positively specified
       
  2384 			detail['TESTCODE'] = self.CheckValue(evaluator, "TESTCODE", "")
       
  2385 
       
  2386 			# make a key that identifies this platform uniquely
       
  2387 			# - used to tell us whether we have done the pre-processing
       
  2388 			# we need already using another platform with compatible values.
       
  2389 
       
  2390 			key = str(detail['VARIANT_HRH']) \
       
  2391 			 	+ str(detail['EPOCROOT']) \
       
  2392 		    	+ detail['SYSTEMINCLUDE'] \
       
  2393 		    	+ detail['PLATFORM']
       
  2394 
       
  2395 		    # Keep a short version of the key for use in filenames.
       
  2396 			uniq = hashlib.md5()
       
  2397 			uniq.update(key)
       
  2398 
       
  2399 			detail['key'] = key
       
  2400 			detail['key_md5'] = "p_" + uniq.hexdigest()
       
  2401 			del uniq
       
  2402 
       
  2403 			# compare this configuration to the ones we have already seen
       
  2404 
       
  2405 			# Is this an unseen export platform?
       
  2406 			# concatenate all the values we care about in a fixed order
       
  2407 			# and use that as a signature for the exports.
       
  2408 			items = ['EPOCROOT', 'VARIANT_HRH', 'SYSTEMINCLUDE', 'TESTCODE', 'export']
       
  2409 			export = ""
       
  2410 			for i in  items:
       
  2411 				if i in detail:
       
  2412 					export += i + str(detail[i])
       
  2413 
       
  2414 			if export in exports:
       
  2415 				# add this configuration to an existing export platform
       
  2416 				index = exports[export]
       
  2417 				self.ExportPlatforms[index]['configs'].append(buildConfig)
       
  2418 			else:
       
  2419 				# create a new export platform with this configuration
       
  2420 				exports[export] = len(self.ExportPlatforms)
       
  2421 				exp = copy.copy(detail)
       
  2422 				exp['PLATFORM'] = 'EXPORT'
       
  2423 				exp['configs']  = [buildConfig]
       
  2424 				self.ExportPlatforms.append(exp)
       
  2425 
       
  2426 			# Is this an unseen build platform?
       
  2427 			# concatenate all the values we care about in a fixed order
       
  2428 			# and use that as a signature for the platform.
       
  2429 			items = ['PLATFORM', 'EPOCROOT', 'VARIANT_HRH', 'SYSTEMINCLUDE', 'TESTCODE']
       
  2430 			if raptor_utilities.getOSPlatform().startswith("win"):
       
  2431 				items.append('PLATMACROS.WINDOWS')
       
  2432 			else:
       
  2433 				items.append('PLATMACROS.LINUX')
       
  2434 
       
  2435 			items.extend(interfaces)
       
  2436 			platform = ""
       
  2437 			for i in  items:
       
  2438 				if i in detail:
       
  2439 					platform += i + str(detail[i])
       
  2440 
       
  2441 			if platform in platforms:
       
  2442 				# add this configuration to an existing build platform
       
  2443 				index = platforms[platform]
       
  2444 				self.BuildPlatforms[index]['configs'].append(buildConfig)
       
  2445 			else:
       
  2446 				# create a new build platform with this configuration
       
  2447 				platforms[platform] = len(self.BuildPlatforms)
       
  2448 				detail['configs'] = [buildConfig]
       
  2449 				self.BuildPlatforms.append(detail)
       
  2450 
       
  2451 		# one platform is picked as the "default" for extracting things
       
  2452 		# that are supposedly platform independent (e.g. PRJ_PLATFORMS)
       
  2453 		self.defaultPlatform = self.ExportPlatforms[0]
       
  2454 
       
  2455 	def CheckValue(self, evaluator, key, default = None):
       
  2456 		"""extract a value from an evaluator and raise an exception if None.
       
  2457 
       
  2458 		An optional default can be set to replace a None value."""
       
  2459 		value = evaluator.Get(key)
       
  2460 		if value == None:
       
  2461 			if default == None:
       
  2462 				raise MetaDataError("configuration " + evaluator.config.name +
       
  2463 								    " has no variable " + key)
       
  2464 			else:
       
  2465 				return default
       
  2466 		return value
       
  2467 
       
  2468 	def ReadBldInfFiles(self, aFileList, doExportOnly):
       
  2469 		"""Take a list of bld.inf files and return a list of build specs.
       
  2470 
       
  2471 		The returned specification nodes will be suitable for all the build
       
  2472 		configurations under consideration (using Filter nodes where required).
       
  2473 		"""
       
  2474 
       
  2475 		# we need a Filter node per export platform
       
  2476 		exportNodes = []
       
  2477 		for i,ep in enumerate(self.ExportPlatforms):
       
  2478 			filter = raptor_data.Filter("export_" + str(i))
       
  2479 
       
  2480 			# what configurations is this node active for?
       
  2481 			for config in ep['configs']:
       
  2482 				filter.AddConfigCondition(config.name)
       
  2483 
       
  2484 			exportNodes.append(filter)
       
  2485 
       
  2486 		# we need a Filter node per build platform
       
  2487 		platformNodes = []
       
  2488 		for i,bp in enumerate(self.BuildPlatforms):
       
  2489 			filter = raptor_data.Filter("build_" + str(i))
       
  2490 
       
  2491 			# what configurations is this node active for?
       
  2492 			for config in bp['configs']:
       
  2493 				filter.AddConfigCondition(config.name)
       
  2494 
       
  2495 			# platform-wide data
       
  2496 			platformVar = raptor_data.Variant()
       
  2497 			platformVar.AddOperation(raptor_data.Set("PRODUCT_INCLUDE",
       
  2498 													 str(bp['VARIANT_HRH'])))
       
  2499 
       
  2500 			filter.AddVariant(platformVar)
       
  2501 			platformNodes.append(filter)
       
  2502 
       
  2503 		# check that each bld.inf exists and add a Specification node for it
       
  2504 		# to the nodes of the export and build platforms that it supports.
       
  2505 		for bif in aFileList:
       
  2506 			if bif.isFile():
       
  2507 				self.__Raptor.Info("Processing %s", str(bif))
       
  2508 				try:
       
  2509 					self.AddComponentNodes(bif, exportNodes, platformNodes)
       
  2510 
       
  2511 				except MetaDataError, e:
       
  2512 					self.__Raptor.Error(e.Text, bldinf=str(bif))
       
  2513 					if not self.__Raptor.keepGoing:
       
  2514 						return []
       
  2515 			else:
       
  2516 				self.__Raptor.Error("build info file does not exist", bldinf=str(bif))
       
  2517 				if not self.__Raptor.keepGoing:
       
  2518 					return []
       
  2519 
       
  2520 		# now we have the top-level structure in place...
       
  2521 		#
       
  2522 		# <filter exports 1>
       
  2523 		#		<spec bld.inf 1 />
       
  2524 		#		<spec bld.inf 2 />
       
  2525 		#		<spec bld.inf N /> </filter>
       
  2526 		# <filter build 1>
       
  2527 		#		<spec bld.inf 1 />
       
  2528 		#		<spec bld.inf 2 />
       
  2529 		#		<spec bld.inf N /> </filter>
       
  2530 		# <filter build 2>
       
  2531 		#		<spec bld.inf 1 />
       
  2532 		#		<spec bld.inf 2 />
       
  2533 		#		<spec bld.inf N /> </filter>
       
  2534 		# <filter build 3>
       
  2535 		#		<spec bld.inf 1 />
       
  2536 		#		<spec bld.inf 2 />
       
  2537 		#		<spec bld.inf N /> </filter>
       
  2538 		#
       
  2539 		# assuming that every bld.inf builds for every platform and all
       
  2540 		# exports go to the same place. clearly, it is more likely that
       
  2541 		# some filters have less than N child nodes. in bigger builds there
       
  2542 		# will also be more than one export platform.
       
  2543 
       
  2544 		# we now need to process the EXPORTS for all the bld.inf nodes
       
  2545 		# before we can do anything else (because raptor itself must do
       
  2546 		# some exports before the MMP files that include them can be
       
  2547 		# processed).
       
  2548 		for i,p in enumerate(exportNodes):
       
  2549 			exportPlatform = self.ExportPlatforms[i]
       
  2550 			for s in p.GetChildSpecs():
       
  2551 				try:
       
  2552 					self.ProcessExports(s, exportPlatform)
       
  2553 
       
  2554 				except MetaDataError, e:
       
  2555 					self.__Raptor.Error("%s",e.Text)
       
  2556 					if not self.__Raptor.keepGoing:
       
  2557 						return []
       
  2558 
       
  2559 		# this is a switch to return the function at this point if export
       
  2560 		# only option is specified in the run
       
  2561 		if (self.__Raptor.doExportOnly):
       
  2562 			self.__Raptor.Info("Processing Exports only")
       
  2563 			return[]
       
  2564 
       
  2565 		# after exports are done we can look to see if there are any
       
  2566 		# new Interfaces which can be used for EXTENSIONS. Make sure
       
  2567 		# that we only load each cache once as some export platforms
       
  2568 		# may share a directory.
       
  2569 		doneID = {}
       
  2570 		for ep in self.ExportPlatforms:
       
  2571 			flmDir = ep["FLM_EXPORT_DIR"]
       
  2572 			cid = ep["CACHEID"]
       
  2573 			if flmDir.isDir() and not cid in doneID:
       
  2574 				self.__Raptor.cache.Load(flmDir, cid)
       
  2575 			doneID[cid] = True
       
  2576 
       
  2577 		# finally we can process all the other parts of the bld.inf nodes.
       
  2578 		# Keep a list of the projects we were asked to build so that we can
       
  2579 		# tell at the end if there were any we didn't know about.
       
  2580 		self.projectList = list(self.__Raptor.projects)
       
  2581 		for i,p in enumerate(platformNodes):
       
  2582 			buildPlatform = self.BuildPlatforms[i]
       
  2583 			for s in p.GetChildSpecs():
       
  2584 				try:
       
  2585 					self.ProcessTEMs(s, buildPlatform)
       
  2586 					self.ProcessMMPs(s, buildPlatform)
       
  2587 
       
  2588 				except MetaDataError, e:
       
  2589 					self.__Raptor.Error(e.Text)
       
  2590 					if not self.__Raptor.keepGoing:
       
  2591 						return []
       
  2592 
       
  2593 		for badProj in self.projectList:
       
  2594 			self.__Raptor.Warn("Can't find project '%s' in any build info file", badProj)
       
  2595 
       
  2596 		# everything is specified
       
  2597 		return exportNodes + platformNodes
       
  2598 
       
  2599 	def ModuleName(self,aBldInfPath):
       
  2600 		"""Calculate the name of the ROM/emulator batch files that run the tests"""
       
  2601 
       
  2602 		def LeftPortionOf(pth,sep):
       
  2603 			""" Internal function to return portion of str that is to the left of sep. 
       
  2604 			The partition is case-insentive."""
       
  2605 			length = len((pth.lower().partition(sep.lower()))[0])
       
  2606 			return pth[0:length]
       
  2607 			
       
  2608 		modulePath = LeftPortionOf(LeftPortionOf(os.path.dirname(aBldInfPath), "group"), "ongoing")
       
  2609 		moduleName = os.path.basename(modulePath.strip("/"))
       
  2610 		
       
  2611 		# Ensure that ModuleName does not return blank, if the above calculation determines
       
  2612 		# that moduleName is blank
       
  2613 		if moduleName == "" or moduleName.endswith(":"):
       
  2614 			moduleName = "module"
       
  2615 		return moduleName
       
  2616 
       
  2617 
       
  2618 	def AddComponentNodes(self, buildFile, exportNodes, platformNodes):
       
  2619 		"""Add Specification nodes for a bld.inf to the appropriate platforms."""
       
  2620 		bldInfFile = BldInfFile(buildFile, self.__gnucpp, self.__Raptor)
       
  2621 
       
  2622 		specName = self.getSpecName(buildFile, fullPath=True)
       
  2623 
       
  2624 		if isinstance(buildFile, raptor_xml.SystemModelComponent):
       
  2625 			# this component came from a system_definition.xml
       
  2626 			layer = buildFile.GetContainerName("layer")
       
  2627 			component = buildFile.GetContainerName("component")
       
  2628 		else:
       
  2629 			# this is a plain old bld.inf file from the command-line
       
  2630 			layer = ""
       
  2631 			component = ""
       
  2632 
       
  2633 		# exports are independent of build platform
       
  2634 		for i,ep in enumerate(self.ExportPlatforms):
       
  2635 			specNode = raptor_data.Specification(specName)
       
  2636 
       
  2637 			# keep the BldInfFile object for later
       
  2638 			specNode.bldinf = bldInfFile
       
  2639 
       
  2640 			# add some basic data in a component-wide variant
       
  2641 			var = raptor_data.Variant()
       
  2642 			var.AddOperation(raptor_data.Set("COMPONENT_META", str(buildFile)))
       
  2643 			var.AddOperation(raptor_data.Set("COMPONENT_NAME", component))
       
  2644 			var.AddOperation(raptor_data.Set("COMPONENT_LAYER", layer))
       
  2645 			specNode.AddVariant(var)
       
  2646 
       
  2647 			# add this bld.inf Specification to the export platform
       
  2648 			exportNodes[i].AddChild(specNode)
       
  2649 
       
  2650 		# get the relevant build platforms
       
  2651 		listedPlatforms = bldInfFile.getBuildPlatforms(self.defaultPlatform)
       
  2652 		platforms = getBuildableBldInfBuildPlatforms(listedPlatforms,
       
  2653 													self.__defaultplatforms,
       
  2654 													self.__basedefaultplatforms,
       
  2655 													self.__baseuserdefaultplatforms)
       
  2656 
       
  2657 
       
  2658 
       
  2659 		outputDir = BldInfFile.outputPathFragment(buildFile)
       
  2660 
       
  2661 		# Calculate "module name"
       
  2662 		modulename = self.ModuleName(str(buildFile))
       
  2663 
       
  2664 		for i,bp in enumerate(self.BuildPlatforms):
       
  2665 			if bp['PLATFORM'] in platforms:
       
  2666 				specNode = raptor_data.Specification(specName)
       
  2667 
       
  2668 				# keep the BldInfFile object for later
       
  2669 				specNode.bldinf = bldInfFile
       
  2670 
       
  2671 				# add some basic data in a component-wide variant
       
  2672 				var = raptor_data.Variant()
       
  2673 				var.AddOperation(raptor_data.Set("COMPONENT_META",str(buildFile)))
       
  2674 				var.AddOperation(raptor_data.Set("COMPONENT_NAME", component))
       
  2675 				var.AddOperation(raptor_data.Set("COMPONENT_LAYER", layer))
       
  2676 				var.AddOperation(raptor_data.Set("MODULE", modulename))
       
  2677 				var.AddOperation(raptor_data.Append("OUTPUTPATHOFFSET", outputDir, '/'))
       
  2678 				var.AddOperation(raptor_data.Append("OUTPUTPATH", outputDir, '/'))
       
  2679 				var.AddOperation(raptor_data.Append("BLDINF_OUTPUTPATH",outputDir, '/'))
       
  2680 
       
  2681 				var.AddOperation(raptor_data.Set("TEST_OPTION", specNode.bldinf.getRomTestType(bp)))
       
  2682 				specNode.AddVariant(var)
       
  2683 
       
  2684 				# add this bld.inf Specification to the build platform
       
  2685 				platformNodes[i].AddChild(specNode)
       
  2686 
       
  2687 	def ProcessExports(self, componentNode, exportPlatform):
       
  2688 		"""Do the exports for a given platform and skeleton bld.inf node.
       
  2689 
       
  2690 		This will actually perform exports as certain types of files (.mmh)
       
  2691 		are required to be in place before the rest of the bld.inf node
       
  2692 		(and parts of other bld.inf nodes) can be processed.
       
  2693 
       
  2694 		[some MMP files #include exported .mmh files]
       
  2695 		"""
       
  2696 		if exportPlatform["TESTCODE"]:
       
  2697 			exports = componentNode.bldinf.getTestExports(exportPlatform)
       
  2698 		else:
       
  2699 			exports = componentNode.bldinf.getExports(exportPlatform)
       
  2700 
       
  2701 		self.__Raptor.Debug("%i exports for %s",
       
  2702 							len(exports), str(componentNode.bldinf.filename))
       
  2703 		if exports:
       
  2704 
       
  2705 			# each export is either a 'copy' or 'unzip'
       
  2706 			# maybe we should trap multiple exports to the same location here?
       
  2707 			epocroot = str(exportPlatform["EPOCROOT"])
       
  2708 			bldinf_filename = str(componentNode.bldinf.filename)
       
  2709 			exportwhatlog="<whatlog bldinf='%s' mmp='' config=''>\n" % bldinf_filename
       
  2710 			for export in exports:
       
  2711 				expSrc = export.getSource()
       
  2712 				expDstList = export.getDestination() # Might not be a list in all circumstances
       
  2713 
       
  2714 				# make it a list if it isn't
       
  2715 				if not isinstance(expDstList, list):
       
  2716 					expDstList = [expDstList]
       
  2717 
       
  2718 				fromFile = generic_path.Path(expSrc.replace("$(EPOCROOT)", epocroot))
       
  2719 
       
  2720 				# For each destination in the destination list, add an export target, perform it if required.
       
  2721 				# This ensures that make knows the dependency situation but that the export is made
       
  2722 				# before any other part of the metadata requires it.  It also helps with the build
       
  2723 				# from clean situation where we can't use order only prerequisites.
       
  2724 				for expDst in expDstList:
       
  2725 					toFile = generic_path.Path(expDst.replace("$(EPOCROOT)", epocroot))
       
  2726 					try:
       
  2727 						if export.getAction() == "copy":
       
  2728 							# export the file
       
  2729 							exportwhatlog += self.CopyExport(fromFile, toFile, bldinf_filename)
       
  2730 						else:
       
  2731 							# unzip the zip
       
  2732 							exportwhatlog += ("<archive zipfile='" + str(fromFile) + "'>\n")
       
  2733 							members = self.UnzipExport(fromFile, toFile,
       
  2734 									str(exportPlatform['SBS_BUILD_DIR']),
       
  2735 									bldinf_filename)
       
  2736 							if members != None:
       
  2737 								exportwhatlog += members
       
  2738 							exportwhatlog += "</archive>\n"
       
  2739 					except MetaDataError, e:
       
  2740 						if self.__Raptor.keepGoing:
       
  2741 							self.__Raptor.Error("%s",e.Text, bldinf=bldinf_filename)
       
  2742 						else:
       
  2743 							raise e
       
  2744 			exportwhatlog+="</whatlog>\n"
       
  2745 			self.__Raptor.PrintXML("%s",exportwhatlog)
       
  2746 
       
  2747 	def CopyExport(self, _source, _destination, bldInfFile):
       
  2748 		"""Copy the source file to the destination file (create a directory
       
  2749 		   to copy into if it does not exist). Don't copy if the destination
       
  2750 		   file exists and has an equal or newer modification time."""
       
  2751 		source = generic_path.Path(str(_source).replace('%20',' '))
       
  2752 		destination = generic_path.Path(str(_destination).replace('%20',' '))
       
  2753 		dest_str = str(destination)
       
  2754 		source_str = str(source)
       
  2755 
       
  2756 		exportwhatlog="<export destination='" + dest_str + "' source='" + \
       
  2757 				source_str + "'/>\n"
       
  2758 
       
  2759 		try:
       
  2760 
       
  2761 
       
  2762 			destDir = destination.Dir()
       
  2763 			if not destDir.isDir():
       
  2764 				os.makedirs(str(destDir))
       
  2765 				shutil.copyfile(source_str, dest_str)
       
  2766 				return exportwhatlog
       
  2767 
       
  2768 			sourceMTime = 0
       
  2769 			destMTime = 0
       
  2770 			try:
       
  2771 				sourceMTime = os.stat(source_str)[stat.ST_MTIME]
       
  2772 				destMTime = os.stat(dest_str)[stat.ST_MTIME]
       
  2773 			except OSError, e:
       
  2774 				if sourceMTime == 0:
       
  2775 					message = "Source of export does not exist:  " + str(source)
       
  2776 					if not self.__Raptor.keepGoing:
       
  2777 						raise MetaDataError(message)
       
  2778 					else:
       
  2779 						self.__Raptor.Error(message, bldinf=bldInfFile)
       
  2780 
       
  2781 			if destMTime == 0 or destMTime < sourceMTime:
       
  2782 				if os.path.exists(dest_str):
       
  2783 					os.chmod(dest_str,stat.S_IREAD | stat.S_IWRITE)
       
  2784 				shutil.copyfile(source_str, dest_str)
       
  2785 				self.__Raptor.Info("Copied %s to %s", source_str, dest_str)
       
  2786 			else:
       
  2787 				self.__Raptor.Info("Up-to-date: %s", dest_str)
       
  2788 
       
  2789 
       
  2790 		except Exception,e:
       
  2791 			message = "Could not export " + source_str + " to " + dest_str + " : " + str(e)
       
  2792 			if not self.__Raptor.keepGoing:
       
  2793 				raise MetaDataError(message)
       
  2794 			else:
       
  2795 				self.__Raptor.Error(message, bldinf=bldInfFile)
       
  2796 
       
  2797 		return exportwhatlog
       
  2798 
       
  2799 
       
  2800 	def UnzipExport(self, _source, _destination, _sbs_build_dir, bldinf_filename):
       
  2801 		"""Unzip the source zipfile into the destination directory
       
  2802 		   but only if the markerfile does not already exist there
       
  2803 		   or it does exist but is older than the zipfile.
       
  2804 		   the markerfile is comprised of the name of the zipfile
       
  2805 		   with the ".zip" removed and ".unzipped" added.
       
  2806 		"""
       
  2807 
       
  2808 		# Insert spaces into file if they are there
       
  2809 		source = str(_source).replace('%20',' ')
       
  2810 		destination = str(_destination).replace('%20',' ')
       
  2811 		sanitisedSource = raptor_utilities.sanitise(source)
       
  2812 		sanitisedDestination = raptor_utilities.sanitise(destination)
       
  2813 
       
  2814 		destination = str(_destination).replace('%20',' ')
       
  2815 		exportwhatlog = ""
       
  2816 
       
  2817 
       
  2818 		try:
       
  2819 			if not _destination.isDir():
       
  2820 				os.makedirs(destination)
       
  2821 
       
  2822 			# Form the directory to contain the unzipped marker files, and make the directory if require.
       
  2823 			markerfiledir = generic_path.Path(_sbs_build_dir)
       
  2824 			if not markerfiledir.isDir():
       
  2825 				os.makedirs(str(markerfiledir))
       
  2826 
       
  2827 			# Form the marker file name and convert to Python string
       
  2828 			markerfilename = str(generic_path.Join(markerfiledir, sanitisedSource + sanitisedDestination + ".unzipped"))
       
  2829 
       
  2830 			# Don't unzip if the marker file is already there or more uptodate
       
  2831 			sourceMTime = 0
       
  2832 			destMTime = 0
       
  2833 			try:
       
  2834 				sourceMTime = os.stat(source)[stat.ST_MTIME]
       
  2835 				destMTime = os.stat(markerfilename)[stat.ST_MTIME]
       
  2836 			except OSError, e:
       
  2837 				if sourceMTime == 0:
       
  2838 					raise MetaDataError("Source zip for export does not exist:  " + source)
       
  2839 			if destMTime != 0 and destMTime >= sourceMTime:
       
  2840 				# This file has already been unzipped. Print members then return
       
  2841 				exportzip = zipfile.ZipFile(source, 'r')
       
  2842 				files = exportzip.namelist()
       
  2843 				files.sort()
       
  2844 
       
  2845 				for file in files:
       
  2846 					if not file.endswith('/'):
       
  2847 						expfilename = str(generic_path.Join(destination, file))
       
  2848 						exportwhatlog += "<member>" + expfilename + "</member>\n"
       
  2849 
       
  2850 				self.__Raptor.PrintXML("<clean bldinf='" + bldinf_filename + "' mmp='' config=''>\n")
       
  2851 				self.__Raptor.PrintXML("<zipmarker>" + markerfilename + "</zipmarker>\n")
       
  2852 				self.__Raptor.PrintXML("</clean>\n")
       
  2853 
       
  2854 				return exportwhatlog
       
  2855 
       
  2856 			exportzip = zipfile.ZipFile(source, 'r')
       
  2857 			files = exportzip.namelist()
       
  2858 			files.sort()
       
  2859 			filecount = 0
       
  2860 			for file in files:
       
  2861 				expfilename = str(generic_path.Join(destination, file))
       
  2862 				if file.endswith('/'):
       
  2863 					try:
       
  2864 						os.makedirs(expfilename)
       
  2865 					except OSError, e:
       
  2866 						pass # errors to do with "already exists" are not interesting.
       
  2867 				else:
       
  2868 					try:
       
  2869 						os.makedirs(os.path.split(expfilename)[0])
       
  2870 					except OSError, e:
       
  2871 						pass # errors to do with "already exists" are not interesting.
       
  2872 
       
  2873 					try:
       
  2874 						if os.path.exists(expfilename):
       
  2875 							os.chmod(expfilename,stat.S_IREAD | stat.S_IWRITE)
       
  2876 						expfile = open(expfilename, 'wb')
       
  2877 						expfile.write(exportzip.read(file))
       
  2878 						expfile.close()
       
  2879 						# Each file keeps its modified time the same as what it was before unzipping
       
  2880 						accesstime = time.time()
       
  2881 						datetime = exportzip.getinfo(file).date_time
       
  2882 						timeTuple=(int(datetime[0]), int(datetime[1]), int(datetime[2]), int(datetime[3]), \
       
  2883 									int(datetime[4]), int(datetime[5]), int(0), int(0), int(0))
       
  2884 						modifiedtime = time.mktime(timeTuple)
       
  2885 						os.utime(expfilename,(accesstime, modifiedtime))
       
  2886 
       
  2887 						filecount += 1
       
  2888 						exportwhatlog+="<member>" + expfilename + "</member>\n"
       
  2889 					except IOError, e:
       
  2890 						message = "Could not unzip %s to %s: file %s: %s" %(source, destination, expfilename, str(e))
       
  2891 						if not self.__Raptor.keepGoing:
       
  2892 							raise MetaDataError(message)
       
  2893 						else:
       
  2894 							self.__Raptor.Error(message, bldinf=bldinf_filename)
       
  2895 
       
  2896 			markerfile = open(markerfilename, 'wb+')
       
  2897 			markerfile.close()
       
  2898 			self.__Raptor.PrintXML("<clean bldinf='" + bldinf_filename + "' mmp='' config=''>\n")
       
  2899 			self.__Raptor.PrintXML("<zipmarker>" + markerfilename +	"</zipmarker>\n")
       
  2900 			self.__Raptor.PrintXML("</clean>\n")
       
  2901 
       
  2902 		except IOError:
       
  2903 			self.__Raptor.Warn("Problem while unzipping export %s to %s: %s",source,destination,str(e))
       
  2904 
       
  2905 		self.__Raptor.Info("Unzipped %d files from %s to %s", filecount, source, destination)
       
  2906 		return exportwhatlog
       
  2907 
       
  2908 	def ProcessTEMs(self, componentNode, buildPlatform):
       
  2909 		"""Add Template Extension Makefile nodes for a given platform
       
  2910 		   to a skeleton bld.inf node.
       
  2911 
       
  2912 		This happens after exports have been handled.
       
  2913 		"""
       
  2914 		if buildPlatform["ISFEATUREVARIANT"]:
       
  2915 			return	# feature variation does not run extensions at all
       
  2916 		
       
  2917 		if buildPlatform["TESTCODE"]:
       
  2918 			extensions = componentNode.bldinf.getTestExtensions(buildPlatform)
       
  2919 		else:
       
  2920 			extensions = componentNode.bldinf.getExtensions(buildPlatform)
       
  2921 
       
  2922 		self.__Raptor.Debug("%i template extension makefiles for %s",
       
  2923 							len(extensions), str(componentNode.bldinf.filename))
       
  2924 
       
  2925 		for i,extension in enumerate(extensions):
       
  2926 			if self.__Raptor.projects:
       
  2927 				if not extension.nametag in self.__Raptor.projects:
       
  2928 					self.__Raptor.Debug("Skipping %s", extension.getMakefile())
       
  2929 					continue
       
  2930 				elif extension.nametag in self.projectList:
       
  2931 					self.projectList.remove(extension.nametag)
       
  2932 
       
  2933 			extensionSpec = raptor_data.Specification("extension" + str(i))
       
  2934 
       
  2935 			interface = buildPlatform["extension"]
       
  2936 			customInterface = False
       
  2937 
       
  2938 			# is there an FLM replacement for this extension?
       
  2939 			if extension.interface:
       
  2940 				try:
       
  2941 					interface = self.__Raptor.cache.FindNamedInterface(extension.interface, buildPlatform["CACHEID"])
       
  2942 					customInterface = True
       
  2943 				except KeyError:
       
  2944 					# no, there isn't an FLM
       
  2945 					pass
       
  2946 
       
  2947 			extensionSpec.SetInterface(interface)
       
  2948 
       
  2949 			var = raptor_data.Variant()
       
  2950 			var.AddOperation(raptor_data.Set("EPOCBLD", "$(OUTPUTPATH)"))
       
  2951 			var.AddOperation(raptor_data.Set("PLATFORM", buildPlatform["PLATFORM"]))
       
  2952 			var.AddOperation(raptor_data.Set("PLATFORM_PATH", buildPlatform["PLATFORM"].lower()))
       
  2953 			var.AddOperation(raptor_data.Set("CFG", "$(VARIANTTYPE)"))
       
  2954 			var.AddOperation(raptor_data.Set("CFG_PATH", "$(VARIANTTYPE)"))
       
  2955 			var.AddOperation(raptor_data.Set("GENERATEDCPP", "$(OUTPUTPATH)"))
       
  2956 			var.AddOperation(raptor_data.Set("TEMPLATE_EXTENSION_MAKEFILE", extension.getMakefile()))
       
  2957 			var.AddOperation(raptor_data.Set("TEMCOUNT", str(i)))
       
  2958 
       
  2959 			# Extension inputs are added to the build spec.
       
  2960 			# '$'s are escaped so that they are not expanded by Raptor or
       
  2961 			# by Make in the call to the FLM
       
  2962 			# The Extension makefiles are supposed to expand them themselves
       
  2963 			# Path separators need not be parameterised anymore
       
  2964 			# as bash is the standard shell
       
  2965 			standardVariables = extension.getStandardVariables()
       
  2966 			for standardVariable in standardVariables.keys():
       
  2967 				self.__Raptor.Debug("Set %s=%s", standardVariable, standardVariables[standardVariable])
       
  2968 				value = standardVariables[standardVariable].replace('$(', '$$$$(')
       
  2969 				value = value.replace('$/', '/').replace('$;', ':')
       
  2970 				var.AddOperation(raptor_data.Set(standardVariable, value))
       
  2971 
       
  2972 			# . . . as with the standard variables but the names and number
       
  2973 			# of options are not known in advance so we add them to
       
  2974 			# a "structure" that is self-describing
       
  2975 			var.AddOperation(raptor_data.Set("O._MEMBERS", ""))
       
  2976 			options = extension.getOptions()
       
  2977 			for option in options:
       
  2978 				self.__Raptor.Debug("Set %s=%s", option, options[option])
       
  2979 				value = options[option].replace('$(EPOCROOT)', '$(EPOCROOT)/')
       
  2980 				value = value.replace('$(', '$$$$(')
       
  2981 				value = value.replace('$/', '/').replace('$;', ':')
       
  2982 				value = value.replace('$/', '/').replace('$;', ':')
       
  2983 
       
  2984 				if customInterface:
       
  2985 					var.AddOperation(raptor_data.Set(option, value))
       
  2986 				else:
       
  2987 					var.AddOperation(raptor_data.Append("O._MEMBERS", option))
       
  2988 					var.AddOperation(raptor_data.Set("O." + option, value))
       
  2989 
       
  2990 			extensionSpec.AddVariant(var)
       
  2991 			componentNode.AddChild(extensionSpec)
       
  2992 
       
  2993 
       
  2994 	def ProcessMMPs(self, componentNode, buildPlatform):
       
  2995 		"""Add project nodes for a given platform to a skeleton bld.inf node.
       
  2996 
       
  2997 		This happens after exports have been handled.
       
  2998 		"""
       
  2999 		gnuList = []
       
  3000 		makefileList = []
       
  3001 
       
  3002 		if buildPlatform["TESTCODE"]:
       
  3003 			MMPList = componentNode.bldinf.getTestMMPList(buildPlatform)
       
  3004 		else:
       
  3005 			MMPList = componentNode.bldinf.getMMPList(buildPlatform)
       
  3006 
       
  3007 		bldInfFile = componentNode.bldinf.filename
       
  3008 
       
  3009 		for mmpFileEntry in MMPList['mmpFileList']:
       
  3010 			projectname = mmpFileEntry.filename.File().lower()
       
  3011 
       
  3012 			if self.__Raptor.projects:
       
  3013 				if not projectname in self.__Raptor.projects:
       
  3014 					self.__Raptor.Debug("Skipping %s", str(mmpFileEntry.filename))
       
  3015 					continue
       
  3016 				elif projectname in self.projectList:
       
  3017 					self.projectList.remove(projectname)
       
  3018 
       
  3019 			foundmmpfile = (mmpFileEntry.filename).FindCaseless()
       
  3020 
       
  3021 			if foundmmpfile == None:
       
  3022 				self.__Raptor.Error("Can't find mmp file '%s'", str(mmpFileEntry.filename), bldinf=str(bldInfFile))
       
  3023 				continue
       
  3024 
       
  3025 			mmpFile = MMPFile(foundmmpfile,
       
  3026 								   self.__gnucpp,
       
  3027 								   bldinf = componentNode.bldinf,
       
  3028 								   log = self.__Raptor)
       
  3029 
       
  3030 			mmpFilename = mmpFile.filename
       
  3031 
       
  3032 			self.__Raptor.Info("Processing %s for platform %s",
       
  3033 							   str(mmpFilename),
       
  3034 							   " + ".join([x.name for x in buildPlatform["configs"]]))
       
  3035 
       
  3036 			# Run the Parser
       
  3037 			# The backend supplies the actions
       
  3038 			content = mmpFile.getContent(buildPlatform)
       
  3039 			backend = MMPRaptorBackend(self.__Raptor, str(mmpFilename), str(bldInfFile))
       
  3040 			parser  = MMPParser(backend)
       
  3041 			parseresult = None
       
  3042 			try:
       
  3043 				parseresult = parser.mmp.parseString(content)
       
  3044 			except ParseException,e:
       
  3045 				self.__Raptor.Debug(e) # basically ignore parse exceptions
       
  3046 
       
  3047 			if (not parseresult) or (parseresult[0] != 'MMP'):
       
  3048 				self.__Raptor.Error("The MMP Parser didn't recognise the mmp file '%s'",
       
  3049 					                str(mmpFileEntry.filename), 
       
  3050 					                bldinf=str(bldInfFile))
       
  3051 				self.__Raptor.Debug(content)
       
  3052 				self.__Raptor.Debug("The parse result was %s", parseresult)
       
  3053 			else:
       
  3054 				backend.finalise(buildPlatform)
       
  3055 
       
  3056 			# feature variation only processes FEATUREVARIANT binaries
       
  3057 			if buildPlatform["ISFEATUREVARIANT"] and not backend.featureVariant:
       
  3058 				continue
       
  3059 			
       
  3060 			# now build the specification tree
       
  3061 			mmpSpec = raptor_data.Specification(self.getSpecName(mmpFilename))
       
  3062 			var = backend.BuildVariant
       
  3063 
       
  3064 			var.AddOperation(raptor_data.Set("PROJECT_META", str(mmpFilename)))
       
  3065 
       
  3066 			# If it is a TESTMMPFILE section, the FLM needs to know about it
       
  3067 			if buildPlatform["TESTCODE"] and (mmpFileEntry.testoption in
       
  3068 					["manual", "auto"]):
       
  3069 
       
  3070 				var.AddOperation(raptor_data.Set("TESTPATH",
       
  3071 						mmpFileEntry.testoption.lower() + ".bat"))
       
  3072 
       
  3073 			# The output path for objects, stringtables and bitmaps specified by
       
  3074 			# this MMP.  Adding in the requested target extension prevents build
       
  3075 			# "fouling" in cases where there are several mmp targets which only differ
       
  3076 			# by the requested extension. e.g. elocl.01 and elocl.18
       
  3077 			var.AddOperation(raptor_data.Append("OUTPUTPATH","$(UNIQUETARGETPATH)",'/'))
       
  3078 
       
  3079 			# If the bld.inf entry for this MMP had the BUILD_AS_ARM option then
       
  3080 			# tell the FLM.
       
  3081 			if mmpFileEntry.armoption:
       
  3082 				var.AddOperation(raptor_data.Set("ALWAYS_BUILD_AS_ARM","1"))
       
  3083 
       
  3084 			# what interface builds this node?
       
  3085 			try:
       
  3086 				interfaceName = buildPlatform[backend.getTargetType()]
       
  3087 				mmpSpec.SetInterface(interfaceName)
       
  3088 			except KeyError:
       
  3089 				self.__Raptor.Error("Unsupported target type '%s' in %s",
       
  3090 								    backend.getTargetType(),
       
  3091 								    str(mmpFileEntry.filename),
       
  3092 								    bldinf=str(bldInfFile))
       
  3093 				continue
       
  3094 
       
  3095 			# Although not part of the MMP, some MMP-based build specs additionally require knowledge of their
       
  3096 			# container bld.inf exported headers
       
  3097 			for export in componentNode.bldinf.getExports(buildPlatform):
       
  3098 				destination = export.getDestination()
       
  3099 				if isinstance(destination, list):
       
  3100 					exportfile = str(destination[0])
       
  3101 				else:
       
  3102 					exportfile = str(destination)
       
  3103 
       
  3104 				if re.search('\.h',exportfile,re.IGNORECASE):
       
  3105 					var.AddOperation(raptor_data.Append("EXPORTHEADERS", str(exportfile)))
       
  3106 
       
  3107 			# now we have something worth adding to the component
       
  3108 			mmpSpec.AddVariant(var)
       
  3109 			componentNode.AddChild(mmpSpec)
       
  3110 
       
  3111 			# resources, stringtables and bitmaps are sub-nodes of this project
       
  3112 			# (do not add these for feature variant builds)
       
  3113 			
       
  3114 			if not buildPlatform["ISFEATUREVARIANT"]:
       
  3115 				# Buildspec for Resource files
       
  3116 				for i,rvar in enumerate(backend.ResourceVariants):
       
  3117 					resourceSpec = raptor_data.Specification('resource' + str(i))
       
  3118 					resourceSpec.SetInterface(buildPlatform['resource'])
       
  3119 					resourceSpec.AddVariant(rvar)
       
  3120 					mmpSpec.AddChild(resourceSpec)
       
  3121 
       
  3122 				# Buildspec for String Tables
       
  3123 				for i,stvar in enumerate(backend.StringTableVariants):
       
  3124 					stringTableSpec = raptor_data.Specification('stringtable' + str(i))
       
  3125 					stringTableSpec.SetInterface(buildPlatform['stringtable'])
       
  3126 					stringTableSpec.AddVariant(stvar)
       
  3127 					mmpSpec.AddChild(stringTableSpec)
       
  3128 
       
  3129 				# Buildspec for Bitmaps
       
  3130 				for i,bvar in enumerate(backend.BitmapVariants):
       
  3131 					bitmapSpec = raptor_data.Specification('bitmap' + str(i))
       
  3132 					bitmapSpec.SetInterface(buildPlatform['bitmap'])
       
  3133 					bitmapSpec.AddVariant(bvar)
       
  3134 					mmpSpec.AddChild(bitmapSpec)
       
  3135 
       
  3136 		# feature variation does not run extensions at all
       
  3137 		# so return without considering .*MAKEFILE sections
       
  3138 		if buildPlatform["ISFEATUREVARIANT"]:
       
  3139 			return
       
  3140 			
       
  3141 		# Build spec for gnumakefile
       
  3142 		for g in MMPList['gnuList']:
       
  3143 			projectname = g.getMakefileName().lower()
       
  3144 
       
  3145 			if self.__Raptor.projects:
       
  3146 				if not projectname in self.__Raptor.projects:
       
  3147 					self.__Raptor.Debug("Skipping %s", str(g.getMakefileName()))
       
  3148 					continue
       
  3149 				elif projectname in self.projectList:
       
  3150 					self.projectList.remove(projectname)
       
  3151 
       
  3152 			self.__Raptor.Debug("%i gnumakefile extension makefiles for %s",
       
  3153 						len(gnuList), str(componentNode.bldinf.filename))
       
  3154 			var = raptor_data.Variant()
       
  3155 			gnuSpec = raptor_data.Specification("gnumakefile " + str(g.getMakefileName()))
       
  3156 			interface = buildPlatform["ext_makefile"]
       
  3157 			gnuSpec.SetInterface(interface)
       
  3158 			gnumakefilePath = raptor_utilities.resolveSymbianPath(str(bldInfFile), g.getMakefileName())
       
  3159 			var.AddOperation(raptor_data.Set("EPOCBLD", "$(OUTPUTPATH)"))
       
  3160 			var.AddOperation(raptor_data.Set("PLATFORM", buildPlatform["PLATFORM"]))
       
  3161 			var.AddOperation(raptor_data.Set("EXTMAKEFILENAME", g.getMakefileName()))
       
  3162 			var.AddOperation(raptor_data.Set("DIRECTORY",g.getMakeDirectory()))
       
  3163 			var.AddOperation(raptor_data.Set("CFG","$(VARIANTTYPE)"))
       
  3164 			standardVariables = g.getStandardVariables()
       
  3165 			for standardVariable in standardVariables.keys():
       
  3166 				self.__Raptor.Debug("Set %s=%s", standardVariable, standardVariables[standardVariable])
       
  3167 				value = standardVariables[standardVariable].replace('$(', '$$$$(')
       
  3168 				value = value.replace('$/', '/').replace('$;', ':')
       
  3169 				var.AddOperation(raptor_data.Set(standardVariable, value))
       
  3170 			gnuSpec.AddVariant(var)
       
  3171 			componentNode.AddChild(gnuSpec)
       
  3172 
       
  3173 		# Build spec for makefile
       
  3174 		for m in MMPList['makefileList']:
       
  3175 			projectname = m.getMakefileName().lower()
       
  3176 
       
  3177 			if self.__Raptor.projects:
       
  3178 				if not projectname in self.__Raptor.projects:
       
  3179 					self.__Raptor.Debug("Skipping %s", str(m.getMakefileName()))
       
  3180 					continue
       
  3181 				elif projectname in self.projectList:
       
  3182 					projectList.remove(projectname)
       
  3183 
       
  3184 			self.__Raptor.Debug("%i makefile extension makefiles for %s",
       
  3185 						len(makefileList), str(componentNode.bldinf.filename))
       
  3186 			var = raptor_data.Variant()
       
  3187 			gnuSpec = raptor_data.Specification("makefile " + str(m.getMakefileName()))
       
  3188 			interface = buildPlatform["ext_makefile"]
       
  3189 			gnuSpec.SetInterface(interface)
       
  3190 			gnumakefilePath = raptor_utilities.resolveSymbianPath(str(bldInfFile), m.getMakefileName())
       
  3191 			var.AddOperation(raptor_data.Set("EPOCBLD", "$(OUTPUTPATH)"))
       
  3192 			var.AddOperation(raptor_data.Set("PLATFORM", buildPlatform["PLATFORM"]))
       
  3193 			var.AddOperation(raptor_data.Set("EXTMAKEFILENAME", m.getMakefileName()))
       
  3194 			var.AddOperation(raptor_data.Set("DIRECTORY",m.getMakeDirectory()))
       
  3195 			var.AddOperation(raptor_data.Set("CFG","$(VARIANTTYPE)"))
       
  3196 			var.AddOperation(raptor_data.Set("USENMAKE","1"))
       
  3197 			standardVariables = m.getStandardVariables()
       
  3198 			for standardVariable in standardVariables.keys():
       
  3199 				self.__Raptor.Debug("Set %s=%s", standardVariable, standardVariables[standardVariable])
       
  3200 				value = standardVariables[standardVariable].replace('$(', '$$$$(')
       
  3201 				value = value.replace('$/', '/').replace('$;', ':')
       
  3202 				var.AddOperation(raptor_data.Set(standardVariable, value))
       
  3203 			gnuSpec.AddVariant(var)
       
  3204 			componentNode.AddChild(gnuSpec)
       
  3205 
       
  3206 	def getSpecName(self, aFileRoot, fullPath=False):
       
  3207 		"""Returns a build spec name: this is the file root (full path
       
  3208 		or simple file name) made safe for use as a file name."""
       
  3209 
       
  3210 		if fullPath:
       
  3211 			specName = str(aFileRoot).replace("/","_")
       
  3212 			specName = specName.replace(":","")
       
  3213 		else:
       
  3214 			specName = aFileRoot.File()
       
  3215 
       
  3216 		return specName.lower()
       
  3217 
       
  3218 	def ApplyOSVariant(self, aBuildUnit, aEpocroot):
       
  3219 		# Form path to kif.xml and path to buildinfo.txt
       
  3220 		kifXmlPath = generic_path.Join(aEpocroot, "epoc32", "data","kif.xml")
       
  3221 		buildInfoTxtPath = generic_path.Join(aEpocroot, "epoc32", "data","buildinfo.txt")
       
  3222 
       
  3223 		# Start with osVersion being None. This variable is a string and does two things:
       
  3224 		# 1) is a representation of the OS version
       
  3225 		# 2) is potentially the name of a variant
       
  3226 		osVersion = None
       
  3227 		if kifXmlPath.isFile(): # kif.xml exists so try to read it
       
  3228 			osVersion = getOsVerFromKifXml(str(kifXmlPath))
       
  3229 			if osVersion != None:
       
  3230 				self.__Raptor.Info("OS version \"%s\" determined from file \"%s\"" % (osVersion, kifXmlPath))
       
  3231 
       
  3232 		# OS version was not determined from the kif.xml, e.g. because it doesn't exist
       
  3233 		# or there was a problem parsing it. So, we fall over to using the buildinfo.txt
       
  3234 		if osVersion == None and buildInfoTxtPath.isFile():
       
  3235 			osVersion = getOsVerFromBuildInfoTxt(str(buildInfoTxtPath))
       
  3236 			if osVersion != None:
       
  3237 				self.__Raptor.Info("OS version \"%s\" determined from file \"%s\"" % (osVersion, buildInfoTxtPath))
       
  3238 
       
  3239 		# If we determined a non-empty string for the OS Version, attempt to apply it
       
  3240 		if osVersion and osVersion in self.__Raptor.cache.variants:
       
  3241 			self.__Raptor.Info("applying the OS variant to the configuration \"%s\"." % aBuildUnit.name)
       
  3242 			aBuildUnit.variants.append(self.__Raptor.cache.variants[osVersion])
       
  3243 		else:
       
  3244 			self.__Raptor.Info("no OS variant for the configuration \"%s\"." % aBuildUnit.name)
       
  3245