#!/usr/bin/env python## scons-time - run SCons timings and collect statistics## A script for running a configuration through SCons with a standard# set of invocations to collect timing and memory statistics and to# capture the results in a consistent set of output files for display# and analysis.### Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation## Permission is hereby granted, free of charge, to any person obtaining# a copy of this software and associated documentation files (the# "Software"), to deal in the Software without restriction, including# without limitation the rights to use, copy, modify, merge, publish,# distribute, sublicense, and/or sell copies of the Software, and to# permit persons to whom the Software is furnished to do so, subject to# the following conditions:## The above copyright notice and this permission notice shall be included# in all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.#from__future__importnested_scopes__revision__="src/script/scons-time.py 2523 2007/12/12 09:37:41 knight"# FIXME: I (Miki) added this so the genscipt.py will work############################################################################### END STANDARD SCons SCRIPT HEADER##############################################################################importgetoptimportglobimportosimportreimportshutilimportstringimportsysimporttempfileimporttimetry:FalseexceptNameError:# Pre-2.2 Python has no False keyword.import__builtin____builtin__.False=not1try:TrueexceptNameError:# Pre-2.2 Python has no True keyword.import__builtin____builtin__.True=not0defmake_temp_file(**kw):try:result=tempfile.mktemp(**kw)exceptTypeError:try:save_template=tempfile.templateprefix=kw['prefix']delkw['prefix']tempfile.template=prefixresult=tempfile.mktemp(**kw)finally:tempfile.template=save_templatereturnresultclassPlotter:defincrement_size(self,largest):""" Return the size of each horizontal increment line for a specified maximum value. This returns a value that will provide somewhere between 5 and 9 horizontal lines on the graph, on some set of boundaries that are multiples of 10/100/1000/etc. """i=largest/5ifnoti:returnlargestmultiplier=1whilei>=10:i=i/10multiplier=multiplier*10returni*multiplierdefmax_graph_value(self,largest):# Round up to next integer.largest=int(largest)+1increment=self.increment_size(largest)return((largest+increment-1)/increment)*incrementclassLine:def__init__(self,points,type,title,label,comment,fmt="%s%s"):self.points=pointsself.type=typeself.title=titleself.label=labelself.comment=commentself.fmt=fmtdefprint_label(self,inx,x,y):ifself.label:print'set label %s "%s" at %s,%s right'%(inx,self.label,x,y)defplot_string(self):ifself.title:title_string='title "%s"'%self.titleelse:title_string='notitle'return"'-' %s with lines lt %s"%(title_string,self.type)defprint_points(self,fmt=None):iffmtisNone:fmt=self.fmtifself.comment:print'# %s'%self.commentforx,yinself.points:printfmt%(x,y)print'e'defget_x_values(self):return[p[0]forpinself.points]defget_y_values(self):return[p[1]forpinself.points]classGnuplotter(Plotter):def__init__(self,title,key_location):self.lines=[]self.title=titleself.key_location=key_locationdefline(self,points,type,title=None,label=None,comment=None,fmt='%s%s'):ifpoints:line=Line(points,type,title,label,comment,fmt)self.lines.append(line)defplot_string(self,line):returnline.plot_string()defvertical_bar(self,x,type,label,comment):ifself.get_min_x()<=xandx<=self.get_max_x():points=[(x,0),(x,self.get_max_x())]self.line(points,type,label,comment)defget_all_x_values(self):result=[]forlineinself.lines:result.extend(line.get_x_values())returnresultdefget_all_y_values(self):result=[]forlineinself.lines:result.extend(line.get_y_values())returnresultdefget_min_x(self):try:returnself.min_xexceptAttributeError:self.min_x=min(self.get_all_x_values())returnself.min_xdefget_max_x(self):try:returnself.max_xexceptAttributeError:self.max_x=max(self.get_all_x_values())returnself.max_xdefget_min_y(self):try:returnself.min_yexceptAttributeError:self.min_y=min(self.get_all_y_values())returnself.min_ydefget_max_y(self):try:returnself.max_yexceptAttributeError:self.max_y=max(self.get_all_y_values())returnself.max_ydefdraw(self):ifnotself.lines:returnifself.title:print'set title "%s"'%self.titleprint'set key %s'%self.key_locationinx=1max_y=self.max_graph_value(self.get_max_y())/2forlineinself.lines:line.print_label(inx,line.points[0][0]-1,max_y)inx+=1plot_strings=[self.plot_string(l)forlinself.lines]print'plot '+', \\\n '.join(plot_strings)forlineinself.lines:line.print_points()defuntar(fname):importtarfiletar=tarfile.open(name=fname,mode='r')fortarinfointar:tar.extract(tarinfo)tar.close()defunzip(fname):importzipfilezf=zipfile.ZipFile(fname,'r')fornameinzf.namelist():dir=os.path.dirname(name)try:os.makedirs(dir)except:passopen(name,'w').write(zf.read(name))defread_tree(dir):defread_files(arg,dirname,fnames):forfninfnames:fn=os.path.join(dirname,fn)ifos.path.isfile(fn):open(fn,'rb').read()os.path.walk('.',read_files,None)defredirect_to_file(command,log):return'%s > %s 2>&1'%(command,log)deftee_to_file(command,log):return'%s 2>&1 | tee %s'%(command,log)classSConsTimer:""" Usage: scons-time SUBCOMMAND [ARGUMENTS] Type "scons-time help SUBCOMMAND" for help on a specific subcommand. Available subcommands: func Extract test-run data for a function help Provides help mem Extract --debug=memory data from test runs obj Extract --debug=count data from test runs time Extract --debug=time data from test runs run Runs a test configuration """name='scons-time'name_spaces=' '*len(name)defmakedict(**kw):returnkwdefault_settings=makedict(aegis='aegis',aegis_project=None,chdir=None,config_file=None,initial_commands=[],key_location='bottom left',orig_cwd=os.getcwd(),outdir=None,prefix='',python='"%s"'%sys.executable,redirect=redirect_to_file,scons=None,scons_flags='--debug=count --debug=memory --debug=time --debug=memoizer',scons_lib_dir=None,scons_wrapper=None,startup_targets='--help',subdir=None,subversion_url=None,svn='svn',svn_co_flag='-q',tar='tar',targets='',targets0=None,targets1=None,targets2=None,title=None,unzip='unzip',verbose=False,vertical_bars=[],unpack_map={'.tar.gz':(untar,'%(tar)s xzf %%s'),'.tgz':(untar,'%(tar)s xzf %%s'),'.tar':(untar,'%(tar)s xf %%s'),'.zip':(unzip,'%(unzip)s%%s'),},)run_titles=['Startup','Full build','Up-to-date build',]run_commands=['%(python)s%(scons_wrapper)s%(scons_flags)s --profile=%(prof0)s%(targets0)s','%(python)s%(scons_wrapper)s%(scons_flags)s --profile=%(prof1)s%(targets1)s','%(python)s%(scons_wrapper)s%(scons_flags)s --profile=%(prof2)s%(targets2)s',]stages=['pre-read','post-read','pre-build','post-build',]stage_strings={'pre-read':'Memory before reading SConscript files:','post-read':'Memory after reading SConscript files:','pre-build':'Memory before building targets:','post-build':'Memory after building targets:',}memory_string_all='Memory 'default_stage=stages[-1]time_strings={'total':'Total build time','SConscripts':'Total SConscript file execution time','SCons':'Total SCons execution time','commands':'Total command execution time',}time_string_all='Total .* time'#def__init__(self):self.__dict__.update(self.default_settings)# Functions for displaying and executing commands.defsubst(self,x,dictionary):try:returnx%dictionaryexceptTypeError:# x isn't a string (it's probably a Python function),# so just return it.returnxdefsubst_variables(self,command,dictionary):""" Substitutes (via the format operator) the values in the specified dictionary into the specified command. The command can be an (action, string) tuple. In all cases, we perform substitution on strings and don't worry if something isn't a string. (It's probably a Python function to be executed.) """try:command+''exceptTypeError:action=command[0]string=command[1]args=command[2:]else:action=commandstring=actionargs=(())action=self.subst(action,dictionary)string=self.subst(string,dictionary)return(action,string,args)def_do_not_display(self,msg,*args):passdefdisplay(self,msg,*args):""" Displays the specified message. Each message is prepended with a standard prefix of our name plus the time. """ifcallable(msg):msg=msg(*args)else:msg=msg%argsifmsgisNone:returnfmt='%s[%s]: %s\n'sys.stdout.write(fmt%(self.name,time.strftime('%H:%M:%S'),msg))def_do_not_execute(self,action,*args):passdefexecute(self,action,*args):""" Executes the specified action. The action is called if it's a callable Python function, and otherwise passed to os.system(). """ifcallable(action):action(*args)else:os.system(action%args)defrun_command_list(self,commands,dict):""" Executes a list of commands, substituting values from the specified dictionary. """commands=[self.subst_variables(c,dict)forcincommands]foraction,string,argsincommands:self.display(string,*args)sys.stdout.flush()status=self.execute(action,*args)ifstatus:sys.exit(status)deflog_display(self,command,log):command=self.subst(command,self.__dict__)iflog:command=self.redirect(command,log)returncommanddeflog_execute(self,command,log):command=self.subst(command,self.__dict__)output=os.popen(command).read()ifself.verbose:sys.stdout.write(output)open(log,'wb').write(output)#defarchive_splitext(self,path):""" Splits an archive name into a filename base and extension. This is like os.path.splitext() (which it calls) except that it also looks for '.tar.gz' and treats it as an atomic extensions. """ifpath.endswith('.tar.gz'):returnpath[:-7],path[-7:]else:returnos.path.splitext(path)defargs_to_files(self,args,tail=None):""" Takes a list of arguments, expands any glob patterns, and returns the last "tail" files from the list. """files=[]forainargs:g=glob.glob(a)g.sort()files.extend(g)iftail:files=files[-tail:]returnfilesdefascii_table(self,files,columns,line_function,file_function=lambdax:x,*args,**kw):header_fmt=' '.join(['%12s']*len(columns))line_fmt=header_fmt+' %s'printheader_fmt%columnsforfileinfiles:t=line_function(file,*args,**kw)diff=len(columns)-len(t)ifdiff>0:t+=['']*difft.append(file_function(file))printline_fmt%tuple(t)defcollect_results(self,files,function,*args,**kw):results={}forfileinfiles:base=os.path.splitext(file)[0]run,index=string.split(base,'-')[-2:]run=int(run)index=int(index)value=function(file,*args,**kw)try:r=results[index]exceptKeyError:r=[]results[index]=rr.append((run,value))returnresultsdefdoc_to_help(self,obj):""" Translates an object's __doc__ string into help text. This strips a consistent number of spaces from each line in the help text, essentially "outdenting" the text to the left-most column. """doc=obj.__doc__ifdocisNone:return''returnself.outdent(doc)deffind_next_run_number(self,dir,prefix):""" Returns the next run number in a directory for the specified prefix. Examines the contents the specified directory for files with the specified prefix, extracts the run numbers from each file name, and returns the next run number after the largest it finds. """x=re.compile(re.escape(prefix)+'-([0-9]+).*')matches=map(lambdae,x=x:x.match(e),os.listdir(dir))matches=filter(None,matches)ifnotmatches:return0run_numbers=map(lambdam:int(m.group(1)),matches)returnint(max(run_numbers))+1defgnuplot_results(self,results,fmt='%s%.3f'):""" Prints out a set of results in Gnuplot format. """gp=Gnuplotter(self.title,self.key_location)indices=results.keys()indices.sort()foriinindices:try:t=self.run_titles[i]exceptIndexError:t='??? %s ???'%iresults[i].sort()gp.line(results[i],i+1,t,None,t,fmt=fmt)forbar_tupleinself.vertical_bars:try:x,type,label,comment=bar_tupleexceptValueError:x,type,label=bar_tuplecomment=labelgp.vertical_bar(x,type,None,label,comment)gp.draw()deflogfile_name(self,invocation):""" Returns the absolute path of a log file for the specificed invocation number. """name=self.prefix_run+'-%d.log'%invocationreturnos.path.join(self.outdir,name)defoutdent(self,s):""" Strip as many spaces from each line as are found at the beginning of the first line in the list. """lines=s.split('\n')iflines[0]=='':lines=lines[1:]spaces=re.match(' *',lines[0]).group(0)defstrip_initial_spaces(l,s=spaces):ifl.startswith(spaces):l=l[len(spaces):]returnlreturn'\n'.join([strip_initial_spaces(l)forlinlines])+'\n'defprofile_name(self,invocation):""" Returns the absolute path of a profile file for the specified invocation number. """name=self.prefix_run+'-%d.prof'%invocationreturnos.path.join(self.outdir,name)defset_env(self,key,value):os.environ[key]=value#defget_debug_times(self,file,time_string=None):""" Fetch times from the --debug=time strings in the specified file. """iftime_stringisNone:search_string=self.time_string_allelse:search_string=time_stringcontents=open(file).read()result=re.findall(r'%s: ([\d\.]*)'%search_string,contents)[-4:]result=[float(r)forrinresult]ifnottime_stringisNone:result=result[0]returnresultdefget_function_profile(self,file,function):""" Returns the file, line number, function name, and cumulative time. """try:importpstatsexceptImportError,e:sys.stderr.write('%s: func: %s\n'%(self.name,e))sys.stderr.write('%s This version of Python is missing the profiler.\n'%self.name_spaces)sys.stderr.write('%s Cannot use the "func" subcommand.\n'%self.name_spaces)sys.exit(1)statistics=pstats.Stats(file).statsmatches=[eforeinstatistics.items()ife[0][2]==function]r=matches[0]returnr[0][0],r[0][1],r[0][2],r[1][3]defget_function_time(self,file,function):""" Returns just the cumulative time for the specified function. """returnself.get_function_profile(file,function)[3]defget_memory(self,file,memory_string=None):""" Returns a list of integers of the amount of memory used. The default behavior is to return all the stages. """ifmemory_stringisNone:search_string=self.memory_string_allelse:search_string=memory_stringlines=open(file).readlines()lines=[lforlinlinesifl.startswith(search_string)][-4:]result=[int(l.split()[-1])forlinlines[-4:]]iflen(result)==1:result=result[0]returnresultdefget_object_counts(self,file,object_name,index=None):""" Returns the counts of the specified object_name. """object_string=' '+object_name+'\n'lines=open(file).readlines()line=[lforlinlinesifl.endswith(object_string)][0]result=[int(field)forfieldinline.split()[:4]]ifnotindexisNone:result=result[index]returnresult#command_alias={}defexecute_subcommand(self,argv):""" Executes the do_*() function for the specified subcommand (argv[0]). """ifnotargv:returncmdName=self.command_alias.get(argv[0],argv[0])try:func=getattr(self,'do_'+cmdName)exceptAttributeError:returnself.default(argv)try:returnfunc(argv)exceptTypeError,e:sys.stderr.write("%s%s: %s\n"%(self.name,cmdName,e))importtracebacktraceback.print_exc(file=sys.stderr)sys.stderr.write("Try '%s help %s'\n"%(self.name,cmdName))defdefault(self,argv):""" The default behavior for an unknown subcommand. Prints an error message and exits. """sys.stderr.write('%s: Unknown subcommand "%s".\n'%(self.name,argv[0]))sys.stderr.write('Type "%s help" for usage.\n'%self.name)sys.exit(1)#defdo_help(self,argv):""" """ifargv[1:]:forarginargv[1:]:try:func=getattr(self,'do_'+arg)exceptAttributeError:sys.stderr.write('%s: No help for "%s"\n'%(self.name,arg))else:try:help=getattr(self,'help_'+arg)exceptAttributeError:sys.stdout.write(self.doc_to_help(func))sys.stdout.flush()else:help()else:doc=self.doc_to_help(self.__class__)ifdoc:sys.stdout.write(doc)sys.stdout.flush()returnNone#defhelp_func(self):help="""\ Usage: scons-time func [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT --func=NAME, --function=NAME Report time for function NAME -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """sys.stdout.write(self.outdent(help))sys.stdout.flush()defdo_func(self,argv):""" """format='ascii'function_name='_main'tail=Noneshort_opts='?C:f:hp:t:'long_opts=['chdir=','file=','fmt=','format=','func=','function=','help','prefix=','tail=','title=',]opts,args=getopt.getopt(argv[1:],short_opts,long_opts)foro,ainopts:ifoin('-C','--chdir'):self.chdir=aelifoin('-f','--file'):self.config_file=aelifoin('--fmt','--format'):format=aelifoin('--func','--function'):function_name=aelifoin('-?','-h','--help'):self.do_help(['help','func'])sys.exit(0)elifoin('--max',):max_time=int(a)elifoin('-p','--prefix'):self.prefix=aelifoin('-t','--tail'):tail=int(a)elifoin('--title',):self.title=aifself.config_file:execfile(self.config_file,self.__dict__)ifself.chdir:os.chdir(self.chdir)ifnotargs:pattern='%s*.prof'%self.prefixargs=self.args_to_files([pattern],tail)ifnotargs:ifself.chdir:directory=self.chdirelse:directory=os.getcwd()sys.stderr.write('%s: func: No arguments specified.\n'%self.name)sys.stderr.write('%s No %s*.prof files found in "%s".\n'%(self.name_spaces,self.prefix,directory))sys.stderr.write('%s Type "%s help func" for help.\n'%(self.name_spaces,self.name))sys.exit(1)else:args=self.args_to_files(args,tail)cwd_=os.getcwd()+os.sepifformat=='ascii':defprint_function_timing(file,func):try:f,line,func,time=self.get_function_profile(file,func)exceptValueError,e:sys.stderr.write("%s: func: %s: %s\n"%(self.name,file,e))else:iff.startswith(cwd_):f=f[len(cwd_):]print"%.3f%s:%d(%s)"%(time,f,line,func)forfileinargs:print_function_timing(file,function_name)elifformat=='gnuplot':results=self.collect_results(args,self.get_function_time,function_name)self.gnuplot_results(results)else:sys.stderr.write('%s: func: Unknown format "%s".\n'%(self.name,format))sys.exit(1)#defhelp_mem(self):help="""\ Usage: scons-time mem [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix --stage=STAGE Plot memory at the specified stage: pre-read, post-read, pre-build, post-build (default: post-build) -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """sys.stdout.write(self.outdent(help))sys.stdout.flush()defdo_mem(self,argv):format='ascii'logfile_path=lambdax:xstage=self.default_stagetail=Noneshort_opts='?C:f:hp:t:'long_opts=['chdir=','file=','fmt=','format=','help','prefix=','stage=','tail=','title=',]opts,args=getopt.getopt(argv[1:],short_opts,long_opts)foro,ainopts:ifoin('-C','--chdir'):self.chdir=aelifoin('-f','--file'):self.config_file=aelifoin('--fmt','--format'):format=aelifoin('-?','-h','--help'):self.do_help(['help','mem'])sys.exit(0)elifoin('-p','--prefix'):self.prefix=aelifoin('--stage',):ifnotainself.stages:sys.stderr.write('%s: mem: Unrecognized stage "%s".\n'%(self.name,a))sys.exit(1)stage=aelifoin('-t','--tail'):tail=int(a)elifoin('--title',):self.title=aifself.config_file:execfile(self.config_file,self.__dict__)ifself.chdir:os.chdir(self.chdir)logfile_path=lambdax,c=self.chdir:os.path.join(c,x)ifnotargs:pattern='%s*.log'%self.prefixargs=self.args_to_files([pattern],tail)ifnotargs:ifself.chdir:directory=self.chdirelse:directory=os.getcwd()sys.stderr.write('%s: mem: No arguments specified.\n'%self.name)sys.stderr.write('%s No %s*.log files found in "%s".\n'%(self.name_spaces,self.prefix,directory))sys.stderr.write('%s Type "%s help mem" for help.\n'%(self.name_spaces,self.name))sys.exit(1)else:args=self.args_to_files(args,tail)cwd_=os.getcwd()+os.sepifformat=='ascii':self.ascii_table(args,tuple(self.stages),self.get_memory,logfile_path)elifformat=='gnuplot':results=self.collect_results(args,self.get_memory,self.stage_strings[stage])self.gnuplot_results(results)else:sys.stderr.write('%s: mem: Unknown format "%s".\n'%(self.name,format))sys.exit(1)return0#defhelp_obj(self):help="""\ Usage: scons-time obj [OPTIONS] OBJECT FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix --stage=STAGE Plot memory at the specified stage: pre-read, post-read, pre-build, post-build (default: post-build) -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """sys.stdout.write(self.outdent(help))sys.stdout.flush()defdo_obj(self,argv):format='ascii'logfile_path=lambdax:xstage=self.default_stagetail=Noneshort_opts='?C:f:hp:t:'long_opts=['chdir=','file=','fmt=','format=','help','prefix=','stage=','tail=','title=',]opts,args=getopt.getopt(argv[1:],short_opts,long_opts)foro,ainopts:ifoin('-C','--chdir'):self.chdir=aelifoin('-f','--file'):self.config_file=aelifoin('--fmt','--format'):format=aelifoin('-?','-h','--help'):self.do_help(['help','obj'])sys.exit(0)elifoin('-p','--prefix'):self.prefix=aelifoin('--stage',):ifnotainself.stages:sys.stderr.write('%s: obj: Unrecognized stage "%s".\n'%(self.name,a))sys.stderr.write('%s Type "%s help obj" for help.\n'%(self.name_spaces,self.name))sys.exit(1)stage=aelifoin('-t','--tail'):tail=int(a)elifoin('--title',):self.title=aifnotargs:sys.stderr.write('%s: obj: Must specify an object name.\n'%self.name)sys.stderr.write('%s Type "%s help obj" for help.\n'%(self.name_spaces,self.name))sys.exit(1)object_name=args.pop(0)ifself.config_file:execfile(self.config_file,self.__dict__)ifself.chdir:os.chdir(self.chdir)logfile_path=lambdax,c=self.chdir:os.path.join(c,x)ifnotargs:pattern='%s*.log'%self.prefixargs=self.args_to_files([pattern],tail)ifnotargs:ifself.chdir:directory=self.chdirelse:directory=os.getcwd()sys.stderr.write('%s: obj: No arguments specified.\n'%self.name)sys.stderr.write('%s No %s*.log files found in "%s".\n'%(self.name_spaces,self.prefix,directory))sys.stderr.write('%s Type "%s help obj" for help.\n'%(self.name_spaces,self.name))sys.exit(1)else:args=self.args_to_files(args,tail)cwd_=os.getcwd()+os.sepifformat=='ascii':self.ascii_table(args,tuple(self.stages),self.get_object_counts,logfile_path,object_name)elifformat=='gnuplot':stage_index=0forsinself.stages:ifstage==s:breakstage_index=stage_index+1results=self.collect_results(args,self.get_object_counts,object_name,stage_index)self.gnuplot_results(results)else:sys.stderr.write('%s: obj: Unknown format "%s".\n'%(self.name,format))sys.exit(1)return0#defhelp_run(self):help="""\ Usage: scons-time run [OPTIONS] [FILE ...] --aegis=PROJECT Use SCons from the Aegis PROJECT --chdir=DIR Name of unpacked directory for chdir -f FILE, --file=FILE Read configuration from specified FILE -h, --help Print this help and exit -n, --no-exec No execute, just print command lines --number=NUMBER Put output in files for run NUMBER --outdir=OUTDIR Put output files in OUTDIR -p STRING, --prefix=STRING Use STRING as log file/profile prefix --python=PYTHON Time using the specified PYTHON -q, --quiet Don't print command lines --scons=SCONS Time using the specified SCONS --svn=URL, --subversion=URL Use SCons from Subversion URL -v, --verbose Display output of commands """sys.stdout.write(self.outdent(help))sys.stdout.flush()defdo_run(self,argv):""" """run_number_list=[None]short_opts='?f:hnp:qs:v'long_opts=['aegis=','file=','help','no-exec','number=','outdir=','prefix=','python=','quiet','scons=','svn=','subdir=','subversion=','verbose',]opts,args=getopt.getopt(argv[1:],short_opts,long_opts)foro,ainopts:ifoin('--aegis',):self.aegis_project=aelifoin('-f','--file'):self.config_file=aelifoin('-?','-h','--help'):self.do_help(['help','run'])sys.exit(0)elifoin('-n','--no-exec'):self.execute=self._do_not_executeelifoin('--number',):run_number_list=self.split_run_numbers(a)elifoin('--outdir',):self.outdir=aelifoin('-p','--prefix'):self.prefix=aelifoin('--python',):self.python=aelifoin('-q','--quiet'):self.display=self._do_not_displayelifoin('-s','--subdir'):self.subdir=aelifoin('--scons',):self.scons=aelifoin('--svn','--subversion'):self.subversion_url=aelifoin('-v','--verbose'):self.redirect=tee_to_fileself.verbose=Trueself.svn_co_flag=''ifnotargsandnotself.config_file:sys.stderr.write('%s: run: No arguments or -f config file specified.\n'%self.name)sys.stderr.write('%s Type "%s help run" for help.\n'%(self.name_spaces,self.name))sys.exit(1)ifself.config_file:execfile(self.config_file,self.__dict__)ifargs:self.archive_list=argsarchive_file_name=os.path.split(self.archive_list[0])[1]ifnotself.subdir:self.subdir=self.archive_splitext(archive_file_name)[0]ifnotself.prefix:self.prefix=self.archive_splitext(archive_file_name)[0]prepare=Noneifself.subversion_url:prepare=self.prep_subversion_runelifself.aegis_project:prepare=self.prep_aegis_runforrun_numberinrun_number_list:self.individual_run(run_number,self.archive_list,prepare)defsplit_run_numbers(self,s):result=[]fornins.split(','):try:x,y=n.split('-')exceptValueError:result.append(int(n))else:result.extend(range(int(x),int(y)+1))returnresultdefscons_path(self,dir):returnos.path.join(dir,'src','script','scons.py')defscons_lib_dir_path(self,dir):returnos.path.join(dir,'src','engine')defprep_aegis_run(self,commands,removals):self.aegis_tmpdir=make_temp_file(prefix=self.name+'-aegis-')removals.append((shutil.rmtree,'rm -rf %%s',self.aegis_tmpdir))self.aegis_parent_project=os.path.splitext(self.aegis_project)[0]self.scons=self.scons_path(self.aegis_tmpdir)self.scons_lib_dir=self.scons_lib_dir_path(self.aegis_tmpdir)commands.extend(['mkdir %(aegis_tmpdir)s',(lambda:os.chdir(self.aegis_tmpdir),'cd %(aegis_tmpdir)s'),'%(aegis)s -cp -ind -p %(aegis_parent_project)s .','%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .',])defprep_subversion_run(self,commands,removals):self.svn_tmpdir=make_temp_file(prefix=self.name+'-svn-')removals.append((shutil.rmtree,'rm -rf %%s',self.svn_tmpdir))self.scons=self.scons_path(self.svn_tmpdir)self.scons_lib_dir=self.scons_lib_dir_path(self.svn_tmpdir)commands.extend(['mkdir %(svn_tmpdir)s','%(svn)s co %(svn_co_flag)s -r %(run_number)s%(subversion_url)s%(svn_tmpdir)s',])defindividual_run(self,run_number,archive_list,prepare=None):""" Performs an individual run of the default SCons invocations. """commands=[]removals=[]ifprepare:prepare(commands,removals)save_scons=self.sconssave_scons_wrapper=self.scons_wrappersave_scons_lib_dir=self.scons_lib_dirifself.outdirisNone:self.outdir=self.orig_cwdelifnotos.path.isabs(self.outdir):self.outdir=os.path.join(self.orig_cwd,self.outdir)ifself.sconsisNone:self.scons=self.scons_path(self.orig_cwd)ifself.scons_lib_dirisNone:self.scons_lib_dir=self.scons_lib_dir_path(self.orig_cwd)ifself.scons_wrapperisNone:self.scons_wrapper=self.sconsifnotrun_number:run_number=self.find_next_run_number(self.outdir,self.prefix)self.run_number=str(run_number)self.prefix_run=self.prefix+'-%03d'%run_numberifself.targets0isNone:self.targets0=self.startup_targetsifself.targets1isNone:self.targets1=self.targetsifself.targets2isNone:self.targets2=self.targetsself.tmpdir=make_temp_file(prefix=self.name+'-')commands.extend(['mkdir %(tmpdir)s',(os.chdir,'cd %%s',self.tmpdir),])forarchiveinarchive_list:ifnotos.path.isabs(archive):archive=os.path.join(self.orig_cwd,archive)ifos.path.isdir(archive):dest=os.path.split(archive)[1]commands.append((shutil.copytree,'cp -r %%s %%s',archive,dest))else:suffix=self.archive_splitext(archive)[1]commands.append(self.unpack_map[suffix]+(archive,))commands.extend([(os.chdir,'cd %%s',self.subdir),])commands.extend(self.initial_commands)commands.extend([(lambda:read_tree('.'),'find * -type f | xargs cat > /dev/null'),(self.set_env,'export %%s=%%s','SCONS_LIB_DIR',self.scons_lib_dir),'%(python)s%(scons_wrapper)s --version',])index=0forrun_commandinself.run_commands:setattr(self,'prof%d'%index,self.profile_name(index))c=(self.log_execute,self.log_display,run_command,self.logfile_name(index),)commands.append(c)index=index+1commands.extend([(os.chdir,'cd %%s',self.orig_cwd),])ifnotos.environ.get('PRESERVE'):commands.extend(removals)commands.append((shutil.rmtree,'rm -rf %%s',self.tmpdir))self.run_command_list(commands,self.__dict__)self.scons=save_sconsself.scons_lib_dir=save_scons_lib_dirself.scons_wrapper=save_scons_wrapper#defhelp_time(self):help="""\ Usage: scons-time time [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix -t NUMBER, --tail=NUMBER Only report the last NUMBER files --which=TIMER Plot timings for TIMER: total, SConscripts, SCons, commands. """sys.stdout.write(self.outdent(help))sys.stdout.flush()defdo_time(self,argv):format='ascii'logfile_path=lambdax:xtail=Nonewhich='total'short_opts='?C:f:hp:t:'long_opts=['chdir=','file=','fmt=','format=','help','prefix=','tail=','title=','which=',]opts,args=getopt.getopt(argv[1:],short_opts,long_opts)foro,ainopts:ifoin('-C','--chdir'):self.chdir=aelifoin('-f','--file'):self.config_file=aelifoin('--fmt','--format'):format=aelifoin('-?','-h','--help'):self.do_help(['help','time'])sys.exit(0)elifoin('-p','--prefix'):self.prefix=aelifoin('-t','--tail'):tail=int(a)elifoin('--title',):self.title=aelifoin('--which',):ifnotainself.time_strings.keys():sys.stderr.write('%s: time: Unrecognized timer "%s".\n'%(self.name,a))sys.stderr.write('%s Type "%s help time" for help.\n'%(self.name_spaces,self.name))sys.exit(1)which=aifself.config_file:execfile(self.config_file,self.__dict__)ifself.chdir:os.chdir(self.chdir)logfile_path=lambdax,c=self.chdir:os.path.join(c,x)ifnotargs:pattern='%s*.log'%self.prefixargs=self.args_to_files([pattern],tail)ifnotargs:ifself.chdir:directory=self.chdirelse:directory=os.getcwd()sys.stderr.write('%s: time: No arguments specified.\n'%self.name)sys.stderr.write('%s No %s*.log files found in "%s".\n'%(self.name_spaces,self.prefix,directory))sys.stderr.write('%s Type "%s help time" for help.\n'%(self.name_spaces,self.name))sys.exit(1)else:args=self.args_to_files(args,tail)cwd_=os.getcwd()+os.sepifformat=='ascii':columns=("Total","SConscripts","SCons","commands")self.ascii_table(args,columns,self.get_debug_times,logfile_path)elifformat=='gnuplot':results=self.collect_results(args,self.get_debug_times,self.time_strings[which])self.gnuplot_results(results,fmt='%s%.6f')else:sys.stderr.write('%s: time: Unknown format "%s".\n'%(self.name,format))sys.exit(1)if__name__=='__main__':opts,args=getopt.getopt(sys.argv[1:],'h?V',['help','version'])ST=SConsTimer()foro,ainopts:ifoin('-?','-h','--help'):ST.do_help(['help'])sys.exit(0)elifoin('-V','--version'):sys.stdout.write('scons-time version\n')sys.exit(0)ifnotargs:sys.stderr.write('Type "%s help" for usage.\n'%ST.name)sys.exit(1)ST.execute_subcommand(args)