]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/predef/tools/ci/build_log.py
3 # Copyright 2008 Rene Rivera
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
10 import xml
.dom
.minidom
11 import xml
.dom
.pulldom
12 from xml
.sax
.saxutils
import unescape
, escape
14 from pprint
import pprint
15 from __builtin__
import exit
17 class BuildOutputXMLParsing(object):
19 XML parsing utilities for dealing with the Boost Build output
23 def get_child_data( self
, root
, tag
= None, id = None, name
= None, strip
= False, default
= None ):
24 return self
.get_data(self
.get_child(root
,tag
=tag
,id=id,name
=name
),strip
=strip
,default
=default
)
26 def get_data( self
, node
, strip
= False, default
= None ):
31 data_node
= self
.get_child(node
,tag
='#text')
33 data_node
= self
.get_child(node
,tag
='#cdata-section')
36 data
+= data_node
.data
37 data_node
= data_node
.nextSibling
39 if data_node
.nodeName
!= '#text' \
40 and data_node
.nodeName
!= '#cdata-section':
49 def get_child( self
, root
, tag
= None, id = None, name
= None, type = None ):
50 return self
.get_sibling(root
.firstChild
,tag
=tag
,id=id,name
=name
,type=type)
52 def get_sibling( self
, sibling
, tag
= None, id = None, name
= None, type = None ):
57 found
= found
and type == n
.nodeType
59 found
= found
and tag
== n
.nodeName
60 if (id or name
) and found
:
61 found
= found
and n
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
63 if n
.hasAttribute('id'):
64 found
= found
and n
.getAttribute('id') == id
66 found
= found
and n
.hasAttribute('id') and n
.getAttribute('id') == id
68 found
= found
and n
.hasAttribute('name') and n
.getAttribute('name') == name
74 class BuildOutputProcessor(BuildOutputXMLParsing
):
76 def __init__(self
, inputs
):
78 self
.target_to_test
= {}
85 def add_input(self
, input):
87 Add a single build XML output file to our data.
89 events
= xml
.dom
.pulldom
.parse(input)
91 for (event
,node
) in events
:
92 if event
== xml
.dom
.pulldom
.START_ELEMENT
:
94 if node
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
:
95 x_f
= self
.x_name_(*context
)
97 events
.expandNode(node
)
98 # expanding eats the end element, hence walking us out one level
102 elif event
== xml
.dom
.pulldom
.END_ELEMENT
:
105 def x_name_(self
, *context
, **kwargs
):
110 if not isinstance(c
,xml
.dom
.Node
):
111 suffix
= '_'+c
.replace('-','_').replace('#','_')
113 suffix
= '_'+c
.nodeName
.replace('-','_').replace('#','_')
116 names
= map(lambda x
: x
+suffix
,names
)
119 if hasattr(self
,name
):
120 return (name
,getattr(self
,name
))
123 def x_build_test(self
, node
):
125 Records the initial test information that will eventually
126 get expanded as we process the rest of the results.
129 test_name
= test_node
.getAttribute('name')
130 test_target
= self
.get_child_data(test_node
,tag
='target',strip
=True)
131 ## print ">>> %s %s" %(test_name,test_target)
132 self
.test
[test_name
] = {
133 'library' : "/".join(test_name
.split('/')[0:-1]),
134 'test-name' : test_name
.split('/')[-1],
135 'test-type' : test_node
.getAttribute('type').lower(),
136 'test-program' : self
.get_child_data(test_node
,tag
='source',strip
=True),
137 'target' : test_target
,
138 'info' : self
.get_child_data(test_node
,tag
='info',strip
=True),
142 # Add a lookup for the test given the test target.
143 self
.target_to_test
[self
.test
[test_name
]['target']] = test_name
146 def x_build_targets_target( self
, node
):
148 Process the target dependency DAG into an ancestry tree so we can look up
149 which top-level library and test targets specific build actions correspond to.
152 name
= self
.get_child_data(target_node
,tag
='name',strip
=True)
153 path
= self
.get_child_data(target_node
,tag
='path',strip
=True)
154 jam_target
= self
.get_child_data(target_node
,tag
='jam-target',strip
=True)
155 #~ Map for jam targets to virtual targets.
156 self
.target
[jam_target
] = {
160 #~ Create the ancestry.
161 dep_node
= self
.get_child(self
.get_child(target_node
,tag
='dependencies'),tag
='dependency')
163 child
= self
.get_data(dep_node
,strip
=True)
164 child_jam_target
= '<p%s>%s' % (path
,child
.split('//',1)[1])
165 self
.parent
[child_jam_target
] = jam_target
166 dep_node
= self
.get_sibling(dep_node
.nextSibling
,tag
='dependency')
169 def x_build_action( self
, node
):
171 Given a build action log, process into the corresponding test log and
172 specific test log sub-part.
175 name
= self
.get_child(action_node
,tag
='name')
177 name
= self
.get_data(name
)
178 #~ Based on the action, we decide what sub-section the log
181 if re
.match('[^%]+%[^.]+[.](compile)',name
):
182 action_type
= 'compile'
183 elif re
.match('[^%]+%[^.]+[.](link|archive)',name
):
185 elif re
.match('[^%]+%testing[.](capture-output)',name
):
187 elif re
.match('[^%]+%testing[.](expect-failure|expect-success)',name
):
188 action_type
= 'result'
190 # TODO: Enable to see what other actions can be included in the test results.
192 action_type
= 'other'
193 #~ print "+ [%s] %s %s :: %s" %(action_type,name,'','')
195 #~ Get the corresponding test.
196 (target
,test
) = self
.get_test(action_node
,type=action_type
)
197 #~ Skip action that have no corresponding test as they are
198 #~ regular build actions and don't need to show up in the
199 #~ regression results.
201 ##print "??? [%s] %s %s :: %s" %(action_type,name,target,test)
203 ##print "+++ [%s] %s %s :: %s" %(action_type,name,target,test)
204 #~ Collect some basic info about the action.
206 'command' : self
.get_action_command(action_node
,action_type
),
207 'output' : self
.get_action_output(action_node
,action_type
),
208 'info' : self
.get_action_info(action_node
,action_type
)
210 #~ For the test result status we find the appropriate node
211 #~ based on the type of test. Then adjust the result status
212 #~ accordingly. This makes the result status reflect the
213 #~ expectation as the result pages post processing does not
214 #~ account for this inversion.
215 action
['type'] = action_type
216 if action_type
== 'result':
217 if re
.match(r
'^compile',test
['test-type']):
218 action
['type'] = 'compile'
219 elif re
.match(r
'^link',test
['test-type']):
220 action
['type'] = 'link'
221 elif re
.match(r
'^run',test
['test-type']):
222 action
['type'] = 'run'
223 #~ The result sub-part we will add this result to.
224 if action_node
.getAttribute('status') == '0':
225 action
['result'] = 'succeed'
227 action
['result'] = 'fail'
228 # Add the action to the test.
229 test
['actions'].append(action
)
230 # Set the test result if this is the result action for the test.
231 if action_type
== 'result':
232 test
['result'] = action
['result']
235 def x_build_timestamp( self
, node
):
237 The time-stamp goes to the corresponding attribute in the result.
239 self
.timestamps
.append(self
.get_data(node
).strip())
242 def get_test( self
, node
, type = None ):
244 Find the test corresponding to an action. For testing targets these
245 are the ones pre-declared in the --dump-test option. For libraries
246 we create a dummy test as needed.
248 jam_target
= self
.get_child_data(node
,tag
='jam-target')
249 base
= self
.target
[jam_target
]['name']
251 while target
in self
.parent
:
252 target
= self
.parent
[target
]
253 #~ print "--- TEST: %s ==> %s" %(jam_target,target)
254 #~ main-target-type is a precise indicator of what the build target is
255 #~ originally meant to be.
256 #main_type = self.get_child_data(self.get_child(node,tag='properties'),
257 # name='main-target-type',strip=True)
259 if main_type
== 'LIB' and type:
260 lib
= self
.target
[target
]['name']
261 if not lib
in self
.test
:
263 'library' : re
.search(r
'libs/([^/]+)',lib
).group(1),
264 'test-name' : os
.path
.basename(lib
),
266 'test-program' : os
.path
.basename(lib
),
269 test
= self
.test
[lib
]
271 target_name_
= self
.target
[target
]['name']
272 if self
.target_to_test
.has_key(target_name_
):
273 test
= self
.test
[self
.target_to_test
[target_name_
]]
278 #~ The command executed for the action. For run actions we omit the command
279 #~ as it's just noise.
280 def get_action_command( self
, action_node
, action_type
):
281 if action_type
!= 'run':
282 return self
.get_child_data(action_node
,tag
='command')
286 #~ The command output.
287 def get_action_output( self
, action_node
, action_type
):
288 return self
.get_child_data(action_node
,tag
='output',default
='')
290 #~ Some basic info about the action.
291 def get_action_info( self
, action_node
, action_type
):
293 #~ The jam action and target.
294 info
['name'] = self
.get_child_data(action_node
,tag
='name')
295 info
['path'] = self
.get_child_data(action_node
,tag
='path')
296 #~ The timing of the action.
297 info
['time-start'] = action_node
.getAttribute('start')
298 info
['time-end'] = action_node
.getAttribute('end')
299 info
['time-user'] = action_node
.getAttribute('user')
300 info
['time-system'] = action_node
.getAttribute('system')
301 #~ Testing properties.
302 test_info_prop
= self
.get_child_data(self
.get_child(action_node
,tag
='properties'),name
='test-info')
303 info
['always_show_run_output'] = test_info_prop
== 'always_show_run_output'
304 #~ And for compiles some context that may be hidden if using response files.
305 if action_type
== 'compile':
307 define
= self
.get_child(self
.get_child(action_node
,tag
='properties'),name
='define')
309 info
['define'].append(self
.get_data(define
,strip
=True))
310 define
= self
.get_sibling(define
.nextSibling
,name
='define')
313 class BuildConsoleSummaryReport(object):
315 HEADER
= '\033[35m\033[1m'
322 def __init__(self
, bop
, opt
):
326 self
.summary_info
= {
331 self
.header_print("======================================================================")
332 self
.print_test_log()
334 self
.header_print("======================================================================")
338 return len(self
.summary_info
['failed']) > 0
340 def print_test_log(self
):
341 self
.header_print("Tests run..")
342 self
.header_print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
343 for k
in sorted(self
.bop
.test
.keys()):
344 test
= self
.bop
.test
[k
]
345 if len(test
['actions']) > 0:
346 self
.summary_info
['total'] += 1
347 ##print ">>>> {0}".format(test['test-name'])
349 succeed
= test
['result'] == 'succeed'
351 succeed
= test
['actions'][-1]['result'] == 'succeed'
353 self
.summary_info
['success'] += 1
355 self
.summary_info
['failed'].append(test
)
357 self
.ok_print("[PASS] {0}",k
)
359 self
.fail_print("[FAIL] {0}",k
)
360 for action
in test
['actions']:
361 self
.print_action(succeed
, action
)
363 def print_action(self
, test_succeed
, action
):
365 Print the detailed info of failed or always print tests.
367 #self.info_print(">>> {0}",action.keys())
368 if not test_succeed
or action
['info']['always_show_run_output']:
369 output
= action
['output'].strip()
371 p
= self
.fail_print
if action
['result'] == 'fail' else self
.p_print
373 self
.info_print("({0}) {1}",action
['info']['name'],action
['info']['path'])
375 p("{0}",action
['command'].strip())
377 for line
in output
.splitlines():
378 p("{0}",line
.encode('utf-8'))
380 def print_summary(self
):
381 self
.header_print("")
382 self
.header_print("Testing summary..")
383 self
.header_print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
384 self
.p_print("Total: {0}",self
.summary_info
['total'])
385 self
.p_print("Success: {0}",self
.summary_info
['success'])
387 self
.fail_print("Failed: {0}",len(self
.summary_info
['failed']))
388 for test
in self
.summary_info
['failed']:
389 self
.fail_print(" {0}/{1}",test
['library'],test
['test-name'])
391 def p_print(self
, format
, *args
, **kargs
):
392 print format
.format(*args
,**kargs
)
394 def info_print(self
, format
, *args
, **kargs
):
395 print self
.INFO
+format
.format(*args
,**kargs
)+self
.ENDC
397 def header_print(self
, format
, *args
, **kargs
):
398 print self
.HEADER
+format
.format(*args
,**kargs
)+self
.ENDC
400 def ok_print(self
, format
, *args
, **kargs
):
401 print self
.OK
+format
.format(*args
,**kargs
)+self
.ENDC
403 def warn_print(self
, format
, *args
, **kargs
):
404 print self
.WARNING
+format
.format(*args
,**kargs
)+self
.ENDC
406 def fail_print(self
, format
, *args
, **kargs
):
407 print self
.FAIL
+format
.format(*args
,**kargs
)+self
.ENDC
411 def __init__(self
,args
=None):
412 op
= optparse
.OptionParser(
413 usage
="%prog [options] input+")
414 op
.add_option( '--output',
415 help="type of output to generate" )
416 ( opt
, inputs
) = op
.parse_args(args
)
417 bop
= BuildOutputProcessor(inputs
)
419 if opt
.output
== 'console':
420 output
= BuildConsoleSummaryReport(bop
, opt
)
423 self
.failed
= output
.failed
425 if __name__
== '__main__':