sbsv2/raptor/python/filter_utils.py
changeset 0 044383f39525
child 3 e1eecf4d390d
child 590 360bd6b35136
equal deleted inserted replaced
-1:000000000000 0:044383f39525
       
     1 #
       
     2 # Copyright (c) 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 # Classes, methods and regex available for use in log filters
       
    16 #
       
    17 
       
    18 
       
    19 import re
       
    20 
       
    21 
       
    22 # General log structure
       
    23 logTag = re.compile('</?(?P<name>\?xml|buildlog|info|warning|error|recipe|whatlog|build|export|archive|member|bitmap|resource|stringtable|bmconvcmdfile)[>| ]')
       
    24 logHeader = re.compile('<buildlog sbs_version=[\'|\"](?P<version>.+)[\'|\"] xmlns=[\'|\"](?P<xmlns>.+)[\'|\"] xmlns:xsi=[\'|\"](?P<xsdi>.+)[\'|\"] xsi:schemaLocation=[\'|\"](?P<schemaLocation>.+)[\'|\"]>')
       
    25 clean = re.compile('.*<rm(dir)? (files|dirs)=[\'|\"](?P<removals>.+)[\'|\"] />')
       
    26 exports = re.compile('<info>(Copied|Unzipped (?P<unpacked>\d+) files from) (?P<source>.+) to (?P<destination>.+)</info>')
       
    27 
       
    28 # Tool errors and warnings
       
    29 mwError = re.compile('(.+:\d+:(?! (note|warning):) .+|mw(ld|cc)sym2(.exe)?:(?! (note|warning):) .+ \'.+\' .+)')
       
    30 mwWarning = re.compile('.+:\d+: warning: .+|mw(ld|cc)sym2(.exe)?: warning: .+')
       
    31 
       
    32 
       
    33 class AutoFlushedStream(file):
       
    34 	""" Wrapper for STDOUT/STDERR streams to ensure that a flush is performed
       
    35 	after write methods.
       
    36 	Use to avoid buffering when log output in real time is required."""
       
    37 	
       
    38 	def __init__(self, aStream):
       
    39 		self.__stream = aStream
       
    40     
       
    41 	def write(self, aText):
       
    42 		self.__stream.write(aText)
       
    43 		self.__stream.flush()
       
    44 
       
    45 	def writelines(self, aTextList):
       
    46 		self.__stream.writelines(aTextList)
       
    47 		self.__stream.flush()
       
    48 
       
    49 
       
    50 class RecipeFactory(object):
       
    51 	"Factory class to ease creation of appropriately specialised Recipe objects."
       
    52 	
       
    53 	def newRecipe(self, aLine=None, aCustomIgnore=None):
       
    54 		""" Creates objects of base type Recipe depending on the name
       
    55 		of the recipe being processed."""
       
    56 		
       
    57 		name = ""
       
    58 		header = None
       
    59 		if aLine:
       
    60 			header = Recipe.header.match(aLine)
       
    61 		if header:
       
    62 			name = header.group("name")	
       
    63 		
       
    64 		if name.startswith("win32"):
       
    65 			return Win32Recipe(aLine, aCustomIgnore)
       
    66 		else:
       
    67 			return Recipe(aLine, aCustomIgnore)
       
    68 	
       
    69 
       
    70 class Recipe(object):
       
    71 	""" Recipe base class.
       
    72 	Provides a means to get hold of recipe content in a generic way.
       
    73 	Includes a basic understanding of errors and warnings - sub-classes can
       
    74 	override output, error and warning methods to specialise."""
       
    75 	
       
    76 	# Flags to normalise client access, mapping directly to regex groups
       
    77 	name		= "name"
       
    78 	target		= "target"
       
    79 	host		= "host"
       
    80 	layer		= "layer"
       
    81 	component	= "component"
       
    82 	bldinf		= "bldinf"
       
    83 	mmp			= "mmp"
       
    84 	config		= "config"
       
    85 	platform	= "platform"
       
    86 	phase		= "phase"
       
    87 	source		= "source"
       
    88 	start		= "start"
       
    89 	elapsed		= "elapsed"
       
    90 	exit		= "exit"
       
    91 	code		= "code"
       
    92 	attempts	= "attempts"
       
    93 	
       
    94 	# Basic errors/warnings
       
    95 	error = re.compile('Error: ')
       
    96 	warning = re.compile('Warning: ')
       
    97 	
       
    98 	# Recipe metadata
       
    99 	header  = re.compile('<recipe\s+name=[\'|\"](?P<name>.+)[\'|\"]\s+target=[\'|\"](?P<target>.+)[\'|\"]\s+host=[\'|\"](?P<host>.+)[\'|\"]\s+layer=[\'|\"](?P<layer>.*)[\'|\"]\s+component=[\'|\"](?P<component>.*)[\'|\"]\s+bldinf=[\'|\"](?P<bldinf>.+)[\'|\"]\s+mmp=[\'|\"](?P<mmp>.*)[\'|\"]\s+config=[\'|\"](?P<config>.+)[\'|\"]\s+platform=[\'|\"](?P<platform>.*)[\'|\"]\s+phase=[\'|\"](?P<phase>.+)[\'|\"]\s+source=[\'|\"](?P<source>.*)[\'|\"]\s*>')
       
   100 	call    = re.compile('^\+ (?P<call>.+)$')
       
   101 	status  = re.compile('\<status\s+exit=[\'|\"](?P<exit>(ok|failed|retry))[\'|\"](\s+code=[\'|\"](?P<code>\d+)[\'|\"])?\s+attempt=[\'|\"](?P<attempts>\d+)[\'|\"]\s*\/>')
       
   102 	ignore  = re.compile('<!\[CDATA\[')
       
   103 	time    = re.compile(']]><time\s+start=[\'|\"](?P<start>\d+\.\d+)[\'|\"]\s+elapsed=[\'|\"](?P<elapsed>\d+.\d+)[\'|\"]\s*/>$')
       
   104 	footer  = re.compile('</recipe>$')
       
   105 	
       
   106 	
       
   107 	def __init__(self, aLine=None, aCustomIgnore=None):
       
   108 		"""
       
   109 		@param aLine			Optional first line of a recipe (typically the recipe header)
       
   110 		@param aCustomIgnore	Optional compiled regular expression object listing additional
       
   111 								lines to be ignored in this recipe's output.
       
   112 		"""				
       
   113 		self.__customIgnore = aCustomIgnore	
       
   114 		
       
   115 		self.__detail = {
       
   116 						Recipe.name		:"",
       
   117 						Recipe.target	:"",
       
   118 						Recipe.host		:"",
       
   119 						Recipe.layer	:"",
       
   120 						Recipe.component:"",
       
   121 						Recipe.bldinf	:"",
       
   122 						Recipe.mmp		:"",
       
   123 						Recipe.config	:"",
       
   124 						Recipe.platform	:"",
       
   125 						Recipe.phase	:"",
       
   126 						Recipe.source	:"",
       
   127 						Recipe.start	:"",
       
   128 						Recipe.elapsed	:0.0,
       
   129 						Recipe.exit		:"",
       
   130 						Recipe.code		:0,
       
   131 						Recipe.attempts	:0
       
   132 						}
       
   133 		
       
   134 		self.__calls = []
       
   135 		self.__lines = []
       
   136 		self.__complete = False
       
   137 		
       
   138 		if aLine:
       
   139 			self.addLine(aLine)
       
   140 	
       
   141 	def isComplete(self):
       
   142 		"""Signifies that the recipe footer has been reached, the
       
   143 		recipe is complete and so is in a fit state to be queried."""
       
   144 		return self.__complete
       
   145 
       
   146 	def __storeDetail(self, aMatchObject):
       
   147 		for key in aMatchObject.groupdict().keys():
       
   148 			value = aMatchObject.group(key)
       
   149 			if value:
       
   150 				if (key in [Recipe.code,Recipe.attempts]):
       
   151 					value = int(value)
       
   152 				elif key == Recipe.elapsed:
       
   153 					value = float(value)
       
   154 				self.__detail[key] = value
       
   155 	
       
   156 	def addLine(self, aLine):
       
   157 		"""Add a log line to an existing recipe object, processing anything
       
   158 		that can be examined at this point in time directly."""
       
   159 		if Recipe.ignore.match(aLine) or (self.__customIgnore and self.__customIgnore.match(aLine)):
       
   160 			return
       
   161 
       
   162 		header = Recipe.header.match(aLine)
       
   163 		if header:
       
   164 			self.__storeDetail(header)
       
   165 			return
       
   166 		
       
   167 		call = Recipe.call.match(aLine)
       
   168 		if call:
       
   169 			self.__calls.append(call.group("call"))
       
   170 			return
       
   171 		
       
   172 		time = Recipe.time.match(aLine)
       
   173 		if time:
       
   174 			self.__storeDetail(time)
       
   175 			return
       
   176 		
       
   177 		status = Recipe.status.match(aLine)
       
   178 		if status:
       
   179 			self.__storeDetail(status)
       
   180 			return
       
   181 		
       
   182 		if Recipe.footer.match(aLine):
       
   183 			self.__complete = True
       
   184 			return
       
   185 
       
   186 		self.__lines.append(aLine)
       
   187 	
       
   188 	def getDetail(self, aItem):
       
   189 		"""Retrieve attribute detail from recipe tags.
       
   190 		Class data flags provide known items e.g. getDetail(Recipe.source)"""
       
   191 		if self.__detail.has_key(aItem):
       
   192 			return self.__detail[aItem]
       
   193 		
       
   194 	def getCalls(self):
       
   195 		"Return a list of all '+' prefixed tool calls from this recipe."
       
   196 		return self.__calls
       
   197 	
       
   198 	def isError(self, aLine):
       
   199 		"""Convenience matcher for basic errors.
       
   200 		Override in sub-classes to specialise."""
       
   201 		return True if Recipe.error.match(aLine) else False
       
   202 	
       
   203 	def isWarning(self, aLine):
       
   204 		"""Convenience matcher for basic warnings.
       
   205 		Override in sub-classes to specialise."""
       
   206 		return True if Recipe.warning.match(aLine) else False
       
   207 	
       
   208 	def getOutput(self):
       
   209 		""""Return a list of all output that isn't an error or a warning.
       
   210 		Override in sub-classes to specialise."""
       
   211 		output = []
       
   212 		for line in self.__lines:
       
   213 			if not self.isError(line) and not self.isWarning(line):
       
   214 				output.append(line)
       
   215 		return output
       
   216 	
       
   217 	def getErrors(self):
       
   218 		""""Return a list of all output identified as an error.
       
   219 		Override in sub-classes to specialise."""
       
   220 		errors = []
       
   221 		for line in self.__lines:
       
   222 			if self.isError(line):
       
   223 				errors.append(line)
       
   224 		return errors
       
   225 	
       
   226 	def getWarnings(self):
       
   227 		""""Return a list of all output identified as a warning.
       
   228 		Override in sub-classes to specialise."""
       
   229 		warnings = []
       
   230 		for line in self.__lines:
       
   231 			if self.isWarning(line):
       
   232 				warnings.append(line)
       
   233 		return warnings
       
   234 	
       
   235 	def isSuccess(self):
       
   236 		"Convenience method to get overall recipe status."
       
   237 		return True if self.getDetail(Recipe.exit) == "ok" else False
       
   238 	
       
   239 	
       
   240 class Win32Recipe(Recipe):
       
   241 	"Win32 tailored recipe class."
       
   242 	def isError(self, aLine):
       
   243 		return True if mwError.match(aLine) else False
       
   244 	
       
   245 	def isWarning(self, aLine):
       
   246 		return True if mwWarning.match(aLine) else False
       
   247 
       
   248 
       
   249