#!/usr/bin/env python # vim: set fileencoding=utf-8 import sys import os import subprocess import re import linecache # TODO # This is a list of advanced features we want to add: # Warnings: # Handle format '%ld' expects type 'long int", but argument 2 has type 'size_t' # In a way that a second cursor is being displayed for the argument (if on the same line) # Errors: # "expected ? before numeric constant shall produce multiple cursors # Other: # generically reformat template arguments in a way that is configurable, by # either replacing them completely by <..> or by doing some standard # replacements for standard containers, or or or. Also try to handle a bit # about typedefs. # Add blacklists for error messages that may not have their templates mangled, # since they are about exactly those arguments # Add blacklist of messages that shall not output source code (from configuration) # Cut lines longer than the terminal for cursor output, make curser be in # around the middle, or otherwise some best position that cuts out as little as # possible # make the [-Wwhich-warning-caused-this-warning] parts colorizeable in a different color # for undefined references errors, make an option to suggest a library by # searching in /usr/lib for the missung functions ################################### # # generic free helper functions # ################################### def escape_for_regexp(s): s=s.replace("(","\\(") s=s.replace(")","\\)") s=s.replace("[","\\[") s=s.replace("]","\\]") s=s.replace("*","\\*") s=s.replace(".","\\.") s=s.replace("+","\\+") s=s.replace("$","\\$") return s def minimize_templates(msg): # print "shall beautify %s" % msg # little hack to not confuse operator<< with opening temlpates msg=msg.replace("<<","##OPERATOR##") idx=msg.find("<") sidx=idx open=1 while(idx > 0 and idx < (len(msg)-1)): idx+=1 if(msg[idx] == "<"): open+=1 if(msg[idx] == ">"): open-=1 if(open==0): repl=msg[sidx:idx+1] open=1 msg=msg.replace(repl,"<..>") idx=msg.find("<",sidx+1) sidx=idx # prevent operator>> from wreaking havoc if(open<0): open=0 m=re.search("(\[with .*?=.*?\])",msg) if(m): msg=msg.replace(m.group(1),"") msg=msg.replace("##OPERATOR##","<<") return msg ################################### # # configuration stuff # ################################### class color_map256: def __init__(self): self.escape="\x1b" self.attributes = { "bold": "1", "underline": "4" } # 16 normal terminal colors: # \033[40m to 47m sets backgroud color # \033[30m to 37m sets foreground color # \033[1;XXm sets bold foreground color # 256 xterm colors: # foreground via \033[38;5;XXXm # background via \033[48;5;XXXm self.colors = { "black": "0", "white": "15", "grey" : "240", "gray" : "240", "darkgrey" : "234", "darkgray" : "234", "lightgrey" : "7", "lightgray" : "7", "darkred": "88", "red": "196", "lightred": "9", "darkyellow" : "172", "yellow" : "226", "lightyellow" : "11", "darkgreen" : "22", "green" : "46", "lightgreen" : "120", "darkblue" : "18", "blue" : "21", "lightblue" : "63", "darkcyan" : "6", "cyan" : "39", "lightcyan" : "51", "darkviolet" : "53", "violet" : "127", "lightviolet" : "201" } def _get_attribute(self,code): try: c=self.attributes[code] except KeyError: c=str(int(code)) return c def _get_color(self,code): try: c=self.colors[code] except KeyError: c=str(int(code)) # print "Color %s = %s" % (code,c) return c def color(self,code): if( code == "reset" ): return self.escape + "[0m" m0=re.match(r"\s*([^\s]+)\s+on\s+([^\s]+)\s+([^\s]+)\s*$",code) m1=re.match(r"\s*([^\s]+)\s+on\s+([^\s]+)\s*$",code) m2=re.match(r"\s*([^\s]+)\s*$",code) m3=re.match(r"\s*([^\s]+)\s+([^\s]+)\s*$",code) s="" if(m0): s+=self.escape s+="[" s+=self._get_attribute(m0.group(3)) s+="m" s+=self.escape s+="[38;5;" s+=self._get_color(m0.group(1)) s+="m" s+=self.escape s+="[48;5;" s+=self._get_color(m0.group(2)) s+="m" elif(m1): # print "Color %s on %s" % (m1.group(1),m1.group(2)) s+=self.escape s+="[0m" s+=self.escape s+="[38;5;" s+=self._get_color(m1.group(1)) s+="m" s+=self.escape s+="[48;5;" s+=self._get_color(m1.group(2)) s+="m" elif(m2): s+=self.escape s+="[0m" s+=self.escape s+="[38;5;" s+=self._get_color(m2.group(1)) s+="m" elif(m3): # print "Color %s on %s" % (m1.group(1),m1.group(2)) s+=self.escape s+="[0m" s+=self.escape s+="[" s+=self._get_attribute(m3.group(2)) s+="m" s+=self.escape s+="[38;5;" s+=self._get_color(m3.group(1)) s+="m" else: s+=self.escape s+="[0m" return s class config: def __init__(self): self.cmd="gcc" self.nargv="" self.errorlimit=50 self.terminal_tabsize=8 self.output_src=True self.minimize_templates=True self.minimize_templates=False self.mangle_templates=True # self.mangle_templates=False self.expand_templates=True # self.expand_templates=False self.stdlib=True self.tips=True self.tips=False self.colors = { "verbatim" : "reset", "warning" : "darkyellow", "warningmsg" : "yellow", "warningfile" : "yellow", "warningentity" : "yellow", "warningline" : "yellow", "note" : "blue", "notemsg" : "lightblue", "notefile" : "lightblue", "noteentity" : "lightblue", "noteline" : "lightblue", "error" : "darkred", "errormsg" : "white", "errorfile" : "red", "errorentity" : "red bold", "errorline" : "red bold", "candlist" : "lightblue", "candlistfile" : "lightblue", "candlistmsg" : "lightblue", "candlistentity" : "white", "candlistline" : "lightblue", # macro expansion trace "exptrace" : "lightblue", "exptracefile" : "lightblue", "exptraceentity" : "lightblue", "exptraceline" : "lightblue", # instantiation trace "insttrace" : "lightblue", "insttracefile" : "lightblue", "insttraceentity" : "lightblue", "insttraceline" : "lightblue", # inclusion trace "inctrace" : "lightcyan", "inctracefile" : "lightcyan", "inctraceentity" : "lightcyan", "inctraceline" : "lightcyan", "unknown" : "darkcyan", "identifier" : "green", "function" : "lightblue", "class" : "blue", "source" : "reset", "cursor" : "green", "tips" : "lightcyan" } def get_color(self,type): try: c=self.colors[type] except KeyError: c="reset" return c def seperate_cmd_path( self, cmd ): command="" path="" sl=cmd.rfind("/") if( sl == -1 ): command=cmd else: sl+=1 path=cmd[0:sl] command=cmd[sl:] return (path,command) def find_alternative_command( self, cmd, arg0 ): path=os.getenv("PATH") paths=path.split(":") for p in paths: if not p.endswith("/"): p+="/" alt=p+cmd if(alt != arg0): if( os.access(alt,os.X_OK) ): # print "'%s' != '%s'" % (arg0,alt) return alt def setup_compiler_command( self, argv ): (path,cmd)=self.seperate_cmd_path(argv[0]) if( cmd in [ "colorgcc", "colorgcc.py", "xcolorgcc", "xcolorgcc.py" ] ): self.cmd=argv[1] self.nargv=argv[2:] else: self.cmd=self.find_alternative_command(cmd,argv[0]) self.nargv=argv[1:] # strip arg0 from path before calling smoething, since if this is ccache or so, # it will recursively call us again... path=os.getenv("PATH") paths=path.split(":") argpath=os.path.dirname(argv[0]) argpath+="/" newpath="" for p in paths: if not p.endswith("/"): p+="/" if( p == argpath ): continue if( len(newpath) ): newpath+=":" newpath+=p os.putenv("PATH", newpath) # print "After stripping '%s' from PATH '%s' it is now '%s'" % (argpath,path,newpath) ################################### # # entity classes determining the messages components # ################################### # represents a source code line class sline: def __init__(self,f,l,c = None): self.file=f self.line=l self.column=c self.highlights=[] def __str__(self): s="" s+=self.file s+=":" if(self.line): s+=self.line s+=":" if(self.column): s+=self.column s+=":" return s def output(self,form,tfile,tline,tcol): if(self.file): form.output(tfile,self.file + ":") if(self.line): form.output(tline,self.line + ":") if(self.column): form.output(tcol,self.column+ ":") # represents one entry of an instantiation trace class insttrace: def __init__(self,sl,m): self.sline=sl self.msg=m # print "create new insttrace %s %s" % (sl,m) def len(self): s=str(self.sline) # print "len of %s is %s" % (s,len(s)) return len(s) class exptrace: def __init__(self,sl,m): self.sline=sl self.msg=m # print "create new insttrace %s %s" % (sl,m) def len(self): s=str(self.sline) # print "len of %s is %s" % (s,len(s)) return len(s) # this represents a special location attribute such as "At global scope" or "In # function xyz". Not every message will have one, and it will not always have a # proper line numbering class location: def __init__(self,sl,w,e): self.sline=sl # like "In function" self.where=w #like "void foo(void)" self.entity=e # print "create new location %s %s '%s'" % (sl,w,e) def output(self,form,type): # print("type = '%s'" % type ) if(not self.where): return se="" if( self.entity ): se=" '%s'" % self.entity if(type == "warning"): self.sline.output(form,"warningfile","warningline","warningline") form.output("warning"," " + self.where) form.output("warning","%s:\n" % se) elif(type == "note"): self.sline.output(form,"notefile","noteline","noteline") form.output("note"," " + self.where) form.output("note","%s:\n" % se) else: self.sline.output(form,"errorfile","errorline","errorline") form.output("error"," " + self.where) form.output("error","%s:\n" % se) class capture: def __init__(self): self.buf="" def write(self,str): self.buf+=str def get(self): s=self.buf self.buf="" return s class errwarn: def __init__(self): self.column=None self.suppress_caret=False def output(self,form,f,l,e,w,m): # print "OUTPUT %s" % w self.output_msg(form,f,l,e,w,m) # little hack to prevent duplicate source annotaitons from appearing olds=sys.stdout c=capture() sys.stdout=c self.output_src(form,f,l,e,w,m) s0=c.get() s1=None sys.stdout=olds sys.stdout.write(s0) sys.stdout.flush() if(s1): if(s1 != s0): sys.stdout.write(s1) form.output("reset","") def get_tip(self,msg,fil=None,beaut=None): s="" lk=None m=re.search("ISO C\+\+ forbids declaration of '(.*?)' with no type",msg) if(m): k=m lk=re.search("ISO C\+\+ forbids declaration of '(.*?)' with no type",beaut) if( lk ): k=lk s+="A typical mistake is that '%s' really names a type, but the definition was not included" % k.group(1) return s m=re.match("'(.*?)' was not declared in this scope",msg) if(m): k=m lk=re.match("'(.*?)' was not declared in this scope",beaut) if( lk ): k=lk s+="The compiler could not find a definition of '%s'. A likely cause is that an include is missing, the declaration is in another scope, or a previous error caused the declaration to disappear (in which case you should first fix the previous error)" % k.group(1) return s m=re.match("comparison is always false due to limited range of data type",msg) if(m): s+="A likely cause is comparing an unsigned integer to -1, or comparing an enum with a small maximum value to bigger numbers" return s m=re.match("'(.*?)' is not a member of '(.*?)'",msg) if(m): k=m lk=re.match("'(.*?)' is not a member of '(.*?)'",beaut) if( lk ): k=lk s+="You tried to access '%s' of scope/namespace '%s' but the compiler could not find it there. A common cause is a missing include that defines it" % (k.group(1),k.group(2)) return s m=re.match("wrong number of template arguments \(([0-9]+), should be ([0-9]+)\)",msg) if(m): lk=re.match("wrong number of template arguments \(([0-9]+), should be ([0-9]+)\)",beaut) k=m if( lk ): k=lk s+="You specified %s template parameters, but the template in question takes %s. Sometimes the compiler does not take default parameters into account and you need less than %s" % (k.group(1),k.group(2),k.group(2)) return s m=re.match("no match for '(operator.*?)' in .*",msg) if(m): lk=re.match("no match for '(operator.*?)' in .*",beaut) k=m if( lk ): k=lk s+="The code tried to invoke %s, but it was not found. A common cause is to forget implementing an operator needed by certain standard containers" % k.group(1) return s m=re.match("no matching function for call to '(.*?)'",msg) if(m): lk=re.match("no matching function for call to '(.*?)'",beaut) k=m if( lk ): k=lk s+="While the compiler could find functions with the same name and number of arguments, none matches the signature (or a similar one) '%s'. Make sure you are trying to call the right one and it exists (e.g. include necessary headers)" % minimize_templates(k.group(1)) return s m=re.match("cannot convert '(.*?)' to '(.*?)' for argument '([0-9])+' to '(.*?)'",msg) if(m): lk=re.match("cannot convert '(.*?)' to '(.*?)' for argument '([0-9])+' to '(.*?)'",beaut) k=m if( lk ): k=lk s+="The compiler found one function with the correct name and number of arguments '%s', but when trying to pass argument %s, it failed to convert it from the given '%s' to the expected '%s'. Make sure you mean exactly this function, the correct signature is in scope (e.g. by including the correct headers), and you called the variant with the correct number of arguments" % (k.group(4),k.group(3),k.group(1),k.group(2)) return s m=re.match("declaration of '(.*?)' as non-function",msg) if(m): s+="In your declaration of '%s' you did something wrong in a way that confused the hell out of the compiler, thus it gave this error message meaning not much. This could evn be you used : instead of :: to denote scope. In some cases the compiler is right, and the declaration of '%s' (because it is an operator or such) is known to the compiler as a function, but you declared it as if it was something else." return s m=re.match("invalid use of member \(did you forget the '&' \?\)",msg) if(m): s+="You used a member in a way it syntactically isn't made for. This includes using a member variable as a function, or a memberfunction as an array" return s m=re.search("skipping incompatible (.*?) when searching for ",msg) if(m): lk=re.search("skipping incompatible (.*?) when searching for ",beaut) k=m if( lk ): k=lk s+="The file '%s' was probably compiled with a different architecture (e.g. x86 vs x86_64)" % k.group(1) return s m=re.search("call of overloaded '(.*?)' is ambiguous",msg) if(m): lk=re.search("call of overloaded '(.*?)' is ambiguous",beaut) k=m if( lk ): k=lk s+="You were trying to call '%s', but the compiler found multiple functions with the same name and matching parameters in the same scope. A common cause for this is an overly broad template, or a signature where a parameter is really just a typedef of the parameter of the other candidate function. A list of candidates will be displayed, inspect them carefully" % k.group(1) return s m=re.search("expected ';' before '(.*?)'",msg) if(m): s+="A previous expression was not ended properly. This can happen due to some previous error making something disappear (in which case you should fix those first and then revisit this one), or due to you really forgetting some ';', like at the end of the last class declaration. Also note that this could be in an include, so if you see this immedeately after an include, check that file" return s m=re.search("conflicting types for '(.*?)'",msg) if(m): lk=re.search("conflicting types for '(.*?)'",beaut) k=m if( lk ): k=lk s+="The compiler found multiple declarations for '%s' that do not match in their exact types. Search for the declarations in this file and included headers and check where the mismatch occurs" % k.group(1) return s m=re.search("right shift count >= width of type",msg) if(m): s+="If you right shift a certain number as often as it has bits (or more) you are likely setting it to 0. This is usually not what people want, so maybe check what the datatypes involved are" return s m=re.search("stray '(.*?)' in program",msg) if(m): s+="The compiler found a character not belonging to the basic character set. A common mistake is copy/pasting some code from a website or error message that contains utf-8 versions of ' rather than the ascii variant" return s m=re.search("new types may not be defined in a return type",msg) if(m): s+="This error is often caused by a missing semicolon of a class/struct declared a few lines above the function in question" return s m=re.search("too few arguments for format",msg) if(m): s+="This can be a serious bug; in the case there is no argument for a format string the function will just take garbage from the stack and possibly interpreting it as a pointer. And crash." return s m=re.search("no match for call to '\(boost::_bi::bind_t.*\) \(.*\)'",msg) if(m): s+="This is often an indication that a previous boost::bind invocation has a member function with the wrong types and/or is missing placeholders and/or bound parameters" return s m=re.search("expression list treated as compound expression in mem-initializer",msg) if(m): s+="This is sometimes caused by a bug in gcc, where the real error message should have been that it cannot find the ctor of some member variable that is being called with multiple parameters. Sometimes an attempt will be done with less parameters, and an error about that will be emitted." return s if( fil ): m=re.search(".*\(.*\*\)\(.*\)' is not a class, struct, or union type",msg) n=re.search("boost/bind/bind.hpp",fil) if(m and n): s+="""This is a common indication of that you forgot one or more placeholders ("_1") in a boost::bind call""" return s return s def output_tips(self,msg,form,fil=None,beaut=None): if( form.config.tips): t=self.get_tip(msg,fil.file,beaut) if(t and len(t)>0): form.output("tips",t+"\n") def output_msg(self,form,f,l,e,w,m): self.sline.output(form,f,l,l) form.output(l," ") form.output(e,w) form.output(m,self.msg + "\n") if hasattr(self, 'rawmsg'): self.output_tips(self.rawmsg,form,self.sline,self.msg) else: self.output_tips(self.msg,form,self.sline,self.msg) form.output("reset","") def output_src(self,form,f,l,e,w,m): return # a few error/warning messages that we do not want source being displayed for since it won't make sense m=re.search(r"control reaches end of non-void function",self.msg) if(m): return # get more file information # print "Access file %s" % self.file if(self.sline.file and self.sline.line and os.access(self.sline.file,os.R_OK)): line=linecache.getline(self.sline.file,int(self.sline.line)) line=line.replace("\t","".rjust(form.config.terminal_tabsize)) form.output("source",line) if( line[-1] != "\n" ): form.output("source","\n") # print "len=%s, column=%s" % (len(line),self.column) col=None markers=[] # print "LENLINE %s" % len(line) # print "SELF.SLINE.COLUMN %s" % self.sline.column # if(self.sline.column): # print "COLUMN; YES" # if(len(line) > self.sline.column): # print "LENLINE: YES" if(self.sline.column and len(line) > int(self.sline.column)): col=int(self.sline.column) # print "COL %s" % col # some special error/warning constructs that cotain context # information, mainly for gccs older than 4.5 which do not provide # column information (although 4.5 colum information is of low # quality usually) use_quotes_fallback=True m=re.search(r"suggest parentheses around '(.*?)' within '(.*?)'",self.msg) if(m): markers.append(m.group(1)) markers.append(m.group(2)) use_quotes_fallback=False m=re.search(r"expected.*before.*'(.*?)'",self.msg) if(m): markers.append(m.group(1)) use_quotes_fallback=False m=re.search(r"stray '(.*?)' in program",self.msg) if(m): markers.append(m.group(1)) use_quotes_fallback=False m=re.search(r"'.*?' has no member named '(.*?)'",self.msg) if(m): markers.append(m.group(1)) use_quotes_fallback=False m=re.search(r"no match for '.*?' in '(.*?)'",self.msg) if(m): markers.append(m.group(1)) use_quotes_fallback=False if(use_quotes_fallback): m=re.search(r"[''](.*?)['']",self.msg) if(m): markers.append(m.group(1)) if(col or len(markers)>0): form.output_cursor(line,markers,col) else: form.output("source","\n") form.output("reset","") # maybe a warning or error, at least we could not parse it and output the msg verbatim class unknown(errwarn): def __init__(self,msg): self.msg=msg # print "create new unknown %s" % msg def output_src(self,form,f,l,e,w,m): for msg in self.msglist: form.output(m,msg + "\n") def output_msg(self,form,f,l,e,w,m): return class candidate: def __init__(self,sl,cand): self.cand=cand self.sline=sl # print "created new candidate %s %s" % (sl,cand) def output(self,form,f,l,e,w,m): form.output("note","Candidates are:\n") mlen=0 for (file,line,msg) in self.candlist: s=file+":"+repr(line)+": " if(len(s)>mlen): mlen=len(s) for (file,line,msg) in self.candlist: s=file+":"+repr(line)+": " n=mlen-len(s) form.output("notefile",file + ":") form.output("noteline",repr(line) + ": ") form.output("notemsg","".rjust(n) + msg + "\n") def len(self): s=str(self.sline) # print "len of %s is %s" % (s,len(s)) return len(s) class include: def __init__(self,sl): self.sline=sl # print "created new include %s" % sl def len(self): s=str(self.sline) # print "len of %s is %s" % (s,len(s)) return len(s) class elist: def __init__(self): self.list=[] def add(self,entity): self.list.append(entity) def get_mlen(self): mlen=0 for e in self.list: l=e.len() if(l>mlen): mlen=l return mlen def len(self): return len(self.list) class note(errwarn): def __init__(self,sl,m): errwarn.__init__(self) self.sline=sl self.msg=m # print "create new note %s:%s %s" % (f,l,m) def output(self,form): errwarn.output(self,form,"notefile","noteline","note","note: ","notemsg") def output_msg(self,form,f,l,e,w,m): errwarn.output_msg(self,form,"notefile","noteline","note","note: ","notemsg") def output_src(self,form,f,l,e,w,m): if( re.search(r"'(.*?)'",self.msg) ): errwarn.output_src(self,form,f,l,e,w,m) class plain(errwarn): def __init__(self,sl,m): errwarn.__init__(self) self.sline=sl self.msg=m # print "create new note %s:%s %s" % (f,l,m) def output(self,form): errwarn.output(self,form,"verbatim","verbatim","verbatim","","verbatim") def output_msg(self,form,f,l,e,w,m): errwarn.output_msg(self,form,"verbatim","verbatim","verbatim","","verbatim") def output_src(self,form,f,l,e,w,m): if( re.search(r"'(.*?)'",self.msg) ): errwarn.output_src(self,form,f,l,e,w,m) class error(errwarn): def __init__(self,sl,m,raw): errwarn.__init__(self) self.sline=sl self.msg=m self.rawmsg=raw # print "create new error %s %s" % (sl,m) def output(self,form): errwarn.output(self,form,"errorfile","errorline","error","error: ","errormsg") suppressions = [ [ ".*", "boost/function/function_base.hpp", "dereferencing type-punned pointer will break strict-aliasing rules" ], [ ".*", "boost/spirit/home/qi/detail/construct.hpp", "unused parameter" ], [ ".*", "boost/.*", "typedef.*boost_concept_check.*locally defined but not used" ], [ ".*", "boost/.*", "unused-local-typedef" ], [ ".*", "boost.*program_options.*", "unused parameter.*option_name.*" ], ] class warning(errwarn): def __init__(self,sl,m,raw): errwarn.__init__(self) self.sline=sl self.msg=m self.rawmsg=raw # print "create new warning %s %s" % (sl,m) def suppress( self, loc ): if( loc == None ): entity="" else: entity = str(loc.entity) sl=str(self.sline) # print "LOC: %s" % entity # print "SLINE: %s" % sl # print "MSG: %s" % self.msg for supple in suppressions: m0 = re.search(supple[0],entity) m1 = re.search(supple[1],sl) m2 = re.search(supple[2],self.msg) if( m0 and m1 and m2 ): return True return False def output(self,form): errwarn.output(self,form,"warningfile","warningline","warning","warning: ","warningmsg") # this represents a message that shall be output, no matter what type. It acts # as a collecting entity for all kinds of additional information, since those # could array before the actual error/warning message so we don't know yet what # type it is. class message: def __init__(self): # one of the errwarn classes above self.ew=None # an instance of location class self.loc=None # an inclusion trace, can possible be empty for any message self.inctrace=elist() # an instantiation trace for templates, can also be empty self.insttrace=elist() self.exptrace=elist() # a list of candidates for certain kinds of messages self.candidates=elist() # sometimes gcc outputs a little bit more to some error, we save those # chains here, these must be errwarn instances! self.chain=[] self.suppress_caret = False # the type that resides in ew, can still be unknown for quite a while # XXX Try to get rid of the necessity for this distinction self.type="unknown" def output_inctrace(self,form): if( self.inctrace.len() == 0 ): return mlen=self.inctrace.get_mlen() for inc in self.inctrace.list: l=inc.len() inc.sline.output(form,"inctracefile","inctraceline","inctraceline") form.output("inctracefile","".rjust(mlen-l)) form.output("inctrace"," includes\n") def output_location(self,form): if(not self.loc): return self.loc.output(form,self.type) def output_insttrace(self,form): if( self.insttrace.len() == 0 ): return mlen=self.insttrace.get_mlen() cnt=0 for inst in self.insttrace.list: l=inst.len() inst.sline.output(form,"insttracefile","insttraceline","insttraceline") form.output("insttracefile","".rjust(mlen-l)) cnt+=1 if( cnt == 1 ): form.output("insttrace"," instantiates ") elif( cnt > 1): form.output("insttrace"," which instantiates ") if( inst.msg ): form.output("insttraceentity",inst.msg) form.output("insttraceentity","\n") def output_exptrace(self,form): if( self.exptrace.len() == 0 ): return mlen=self.exptrace.get_mlen() cnt=0 for exp in self.exptrace.list: l=exp.len() exp.sline.output(form,"exptracefile","exptraceline","exptraceline") form.output("exptracefile","".rjust(mlen-l)) cnt+=1 if( cnt == 1 ): form.output("exptrace"," in definition of macro ") elif( cnt > 1): form.output("exptrace"," in expansion of macro ") if( exp.msg ): form.output("exptraceentity",exp.msg) form.output("exptraceentity","\n") def output_message(self,form): if(not self.ew): return self.ew.output(form) def output_chain(self,form): for c in self.chain: c.output(form) def output_candidates(self,form): if( self.candidates.len() == 0 ): return mlen=self.candidates.get_mlen() if( self.candidates.len() == 1 ): form.output("candlistmsg","Candidate is:\n") else: form.output("candlistmsg","Candidates are:\n") for cand in self.candidates.list: l=cand.len() cand.sline.output(form,"candlistfile","candlistline","candlistline") form.output("candlistfile","".rjust(mlen-l)) form.output("candlistentity"," " + cand.cand + "\n") # suppress warnings only def suppress( self ) : if( self.type == "warning" ): return self.ew.suppress(self.loc) return False def output(self,form): if( self.suppress() ): return self.output_inctrace(form) self.output_insttrace(form) self.output_exptrace(form) self.output_location(form) self.output_message(form) self.output_candidates(form) self.output_chain(form) ################################### # # The formatting class that takes care of parsing # ################################### class formatter: def __init__(self,conf): self.linecount=0 self.firstline="" self.colors=color_map256() self.config=conf self.color_reset=self.colors.color("reset") self.last_output=True self.errors=0 self.suppressed_errors=0 self.msg=None self.undefs={} self.special_single=False self.next_is_candidate=False def output_undefined(self,form): if( len(self.undefs) == 0 ): return form.output("errorentity","You have the following undefined symbols:\n") tips={} for s,d in self.undefs.iteritems(): m=re.search("^vtable for",s) if(m): tips["When a vtable is the only undefined symbol, chances are high that you forgot to implement a virtual function of that type (since the vtable is usually emitted in the translation unit in which the last virtual function was implemented, chances are high it is the last virtual function)"]=1 form.output("errormsg",s + "\n") form.output("tips","You may want to check your linker line for a missing library, or maybe the order of static libraries (.a files), since for them the order of linking is important. If you have this error for templates, be aware of that you cannot compile templates in .cpp files, and you have to make the definition of templates available in the same translation unit in which they are instantiated, which makes the header the best place to put them\n") for t,d in tips.iteritems(): form.output("tips",t+"\n") # This function mangles the looks of a string literal so any search # functions that we apply to it will not find the to-be-searched entity # within string literals. This is essential to be able to position a cursor # correctly for the variable d in the codes: # printf("d=%s\n",d); # where otherwise it would likely just find the d in the literal def mangle_sliteral(self,code): next_escape=False in_literal=False ncode="" for c in code: if( c == "\\" ): if( in_literal ): ncode+="#" else: ncode+=c next_escape=True elif( c == "\""): if( not next_escape ): in_literal=not in_literal if( in_literal and next_escape ): ncode+="#" else: ncode+=c next_escape=False elif( in_literal ): ncode += "#" next_escape=False else: ncode+=c next_escape=False # print code # print ncode return ncode def output_cursor(self,line,markers,column): sline=self.mangle_sliteral(line) # print "column=%s" % column columns=[] if(column): column -= 1 # columns.append(column) columns.append(column) for marker in markers: oldmarker=marker remarker=escape_for_regexp(marker) # print "old marker = |%s|" % marker # print "re marker = |%s|" % remarker m=re.search("([^a-zA-Z0-9]+" + remarker + "[^a-zA-Z0-9]+)",sline) if(not m): m=re.search("^(" + remarker + "[^a-zA-Z0-9]+)",sline) if(not m): m=re.search("(" + remarker + "[^a-zA-Z0-9]+)",sline) if(not m): m=re.search("([^a-zA-Z0-9]+" + remarker + ")",sline) if(m): marker=m.group(1) # print "new marker = |%s|" % marker idx=sline.find(marker) # print "new marker found at idx %s" % idx idx=sline.find(oldmarker,idx) # print "old marker found at idx %s" % idx if( idx >= 0): columns.append(idx) columns.sort() # print columns s="" inx=0 last=-1 for col in columns: if(col == last): continue else: last=col ns="" idx=col-inx if( idx == 2 ): ns="--^" elif( idx == 1 ): ns="-^" elif( idx == 0 ): ns="^" else: ns="".rjust(idx-2) ns+="--^" s+=ns inx+=len(ns) s+="\n" self.output("cursor",s) def output(self,type,text): col=self.config.get_color(type) sys.stdout.write( self.colors.color(col)) sys.stdout.write( text) sys.stdout.write( self.color_reset) if not isinstance(sys.stdout,capture): sys.stdout.flush() def output_last_entity(self): self.last_output=True if(self.msg.type == "error"): self.errors+=1 if(self.errors > self.config.errorlimit): self.suppressed_errors+=1 return self.msg.output(self) def start_new_entity_on_message(self): # print("start_new_entity_on_message") if( self.msg ): # if( self.msg.ew or self.special_single ): if( self.msg.ew or self.special_single ): self.special_single=False self.start_new_entity() else: self.start_new_entity() def start_new_entity(self): if(self.msg != None): self.output_last_entity() # print "############ START NEW ENTITY #############" self.msg=message() self.last_output=False def strip_template_args(self,msg,bp): bmsg=msg[0:bp] blevel=1 while(blevel > 0): if( msg[bp] == '<' ): blevel+=1 if( msg[bp] == '>' ): blevel-=1 bp+=1 # print "SKIPPING %s" % msg[obp-1:bp] msg=bmsg+"...>"+msg[bp:] return msg def minimize_single_template(self,msg,typ): add=len(typ)+1 typ+="<" bp=msg.find(typ) while( bp != -1 ): obp=bp+1 bp+=add msg=self.strip_template_args(msg,bp) bp=msg.find(typ,obp) return msg def mangle_templates(self,msg): msg=self.minimize_single_template(msg,"boost::variant") msg=self.minimize_single_template(msg,"vwd::datetime_tag_descriptor") return msg def get_indlevel(self,level): ind="" for i in range(0,level): ind += " " return ind expansion_blacklist = [ ("'<'","LSTOKEN"), ("'>'","RSTOKEN"), ("operator<<","OPERATORLEFTSHIFT"), ("operator>>","OPERATORRIGHTSHIFT"), ("operator<=","OPERATORLE"), ("operator>=","OPERATORGE"), ("operator<","OPERATORLT"), ("operator>","OPERATORGT"), ("operator->","OPERATORPFEIL"), ("->","XPFEIL"), ("","TYPERROR"), ("","ANONYMOUSTYPE"), ("","TEMPLATE_PARAMETER_1_TO_2"), ("","TEMPLATE_PARAMETER_1_TO_3"), ("","TEMPLATE_PARAMETER_1_TO_4"), ("","TEMPLATE_PARAMETER_1_TO_5"), ("","TEMPLATE_PARAMETER_1_TO_6"), ("","TEMPLATE_PARAMETER_1_TO_7"), ("","TEMPLATE_PARAMETER_1_TO_8"), ("","TEMPLATE_PARAMETER_1_TO_9"), ("","TEMPLATE_PARAMETER_1_TO_10"), ] def expand_templates(self,msg): for (blfrom,blto) in self.expansion_blacklist: msg=re.sub(blfrom,"__expansion_blaclist_token%sexpansion_blacklist_token__" % blto,msg) nmsg="" level=0 for c in msg: if( c == '>' ): level-=1 nmsg += "\n" nmsg += self.get_indlevel(level) nmsg += c if( c == '<' ): # if( level == 0 ): # nmsg = nmsg[:-1] # nmsg += "\n<" # else: # nmsg += "\n" nmsg += "\n" level+=1 nmsg += self.get_indlevel(level) # print("nmsg = '%s'" % nmsg ) nmsg=re.sub(r'<\s+\.\.\.\s+>',"<...>",nmsg) # this 8 should be configurable nmsg=re.sub(r'<\s*(.{0,8})\s*>',r"<\1>",nmsg,re.MULTILINE) for (blfrom,blto) in self.expansion_blacklist: nmsg=re.sub("__expansion_blaclist_token%sexpansion_blacklist_token__" % blto,blfrom,nmsg) nmsg=nmsg.split("\n") for i in range(1,len(nmsg)): nmsg[i]=" " + nmsg[i] nmsg="\n".join(nmsg) return nmsg # XXX can be made a free function # XXX Needs a lot of cleanup since it matche sometimes when it should not, and it will likely not work together with generic mangling functions def beautify_stdlib(self,msg): # special cases to make stdlib components even nicer than standard template mangling would do # first simple replacements msg=msg.replace("std::basic_string","std::string") msg=msg.replace("std::basic_string","std::wstring") msg=msg.replace("std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char, _Traits = std::char_traits, _Alloc = std::allocator]","std::string::string(const char*)") msg=msg.replace("std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = wchar_t, _Traits = std::char_traits, _Alloc = std::allocator]","std::wstring::wstring(const wchar_t*)") msg=msg.replace("std::basic_string, std::allocator >","std::string") msg=msg.replace("std::basic_string, std::allocator >","std::wstring") m1=r"std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(" m2=r") [with _CharT = char, _Traits = std::char_traits]" rm=escape_for_regexp(m1)+r"(.*?)"+escape_for_regexp(m2) m=re.search(rm,msg) if(m): msg="std::ostream& std::ostream::operator<<(%s)" % m.group(1) # must be after the broader matches above msg=msg.replace("std::basic_ostream<_CharT, _Traits>","std::ostream") # a tad more complex regexps m=re.search(r"std::vector<(.*?), std::allocator<\1 > >",msg) if(m): msg=msg.replace("std::vector<%s, std::allocator<%s > >" % (m.group(1),m.group(1)), "std::vector<%s>" % m.group(1)) m=re.search(r"std::_Rb_tree_iterator >",msg) if(m): msg=msg.replace("std::_Rb_tree_iterator >" % m.group(1),"std::map<%s>::iterator" % m.group(1)) m=re.search(r"std::_Rb_tree_const_iterator >",msg) if(m): msg=msg.replace("std::_Rb_tree_const_iterator >" % m.group(1),"std::map<%s>::const_iterator" % m.group(1)) m=re.search(r"class std::map<(.*?), (.*?), std::less<\1>, std::allocator > >",msg) if(m): msg=msg.replace("class std::map<%s, %s, std::less<%s>, std::allocator > >" % ( m.group(1), m.group(2), m.group(1), m.group(1), m.group(2) ), "class std::map<%s, %s>" % (m.group(1), m.group(2))) # gcc-4.5 m=re.search("std::map<_Key, _Tp, _Compare, _Alloc>.*?( \[with _Key = (.*?), _Tp = (.*?), _Compare = .*?, _Alloc = .*?, mapped_type = (.*?), key_type = (.*?)\])",msg) if(m): msg=msg.replace(m.group(1),"") msg=msg.replace("std::map<_Key, _Tp, _Compare, _Alloc>","std::map<%s,%s>" % (m.group(2),m.group(3)) ) msg=msg.replace("_Key",m.group(2)) msg=msg.replace("_Tp",m.group(3)) msg=msg.replace("mapped_type",m.group(4)) msg=msg.replace("key_type",m.group(5)) # gcc-4.4 m=re.search("std::map<_Key, _Tp, _Compare, _Alloc>.*?( \[with _Key = (.*?), _Tp = (.*?), _Compare = .*?, _Alloc = .*?\])",msg) if(m): msg=msg.replace(m.group(1),"") msg=msg.replace("std::map<_Key, _Tp, _Compare, _Alloc>","std::map<%s,%s>" % (m.group(2),m.group(3)) ) msg=msg.replace("_Key",m.group(2)) msg=msg.replace("_Tp",m.group(3)) m=re.search("std::basic_string<_CharT, _Traits, _Alloc>::basic_string\(const _CharT\*, const _Alloc&\)( \[with _CharT = (.*?), _Traits = .*?, _Alloc = .*?\])",msg) if(m): msg=msg.replace(m.group(1),"") msg=msg.replace("basic_string<_CharT, _Traits, _Alloc>","basic_string<%s>" % m.group(2)) msg=msg.replace("_CharT",m.group(2)) msg=re.sub(r"'(.*) {aka \1}'",r"'\1'",msg) return msg def beautify_code(self,msg,suppress_caret): m=re.match("^\s*\^$",msg) # print("msg = '%s'" % msg ) # print("suppress_caret = '%s'" % suppress_caret ) if(m): if( suppress_caret ): return "" msg=list(msg) uscores=0 # print("msg = '%s'" % msg ) for i in reversed(range(0,len(msg))): if( msg[i] == '^' ): uscores=2 else: if( uscores > 0): uscores -= 1 msg[i]="_" # print("msg = '%s'" % msg ) msg="".join(msg) msg=self.colors.color( self.config.get_color("cursor") ) + msg + self.color_reset return msg def beautify(self,msg,suppress_caret=False): if(not msg): return msg # print "Have to beautify '%s'" % msg # print("msg = '%s'" % msg ) if(self.config.stdlib): msg=self.beautify_stdlib(msg) # print("msg = '%s'" % msg ) if(self.config.mangle_templates): msg=self.mangle_templates(msg) # print("msg = '%s'" % msg ) if(self.config.minimize_templates): msg=self.minimize_templates(msg) elif(self.config.expand_templates): msg=self.expand_templates(msg) # print "Beautification result '%s'" % msg return msg def add_location(self,sl,where,entity): # print("entity = '%s'" % entity ) # print "add_location()" # all locations introduce a new message entity # self.start_new_entity() entity=self.beautify(entity) self.msg.loc=location(sl,where,entity) def add_error_append(self,sl,msg,suppress_caret=None): rawmsg=msg if( suppress_caret == None ): suppress_caret = self.msg.suppress_caret else: self.msg.suppress_caret = suppress_caret # print("self.msg.suppress_caret = '%s'" % self.msg.suppress_caret ) msg=self.beautify(msg,suppress_caret) err = error(sl,msg,rawmsg) self.msg.chain.append(err) return err def add_undefined_reference(self,ent): self.undefs[ent]=1 def add_unknown(self,msg): # print "add_unknown()" # Unknown messages surely should be messages of their own # self.start_new_entity() # print("msg = '%s'" % msg ) msg=self.beautify(msg) # print("msg = '%s'" % msg ) # self.add_error(sline(None,None,None),msg) if( self.msg is None): self.start_new_entity() self.msg.chain.append(plain(sline(0,0,0),msg)) def add_code(self,msg): # Unknown messages surely should be messages of their own # self.start_new_entity() # print("msg = '%s'" % msg ) msg=self.beautify_code(msg,self.msg.suppress_caret) # print("msg = '%s'" % msg ) # self.add_error(sline(None,None,None),msg) self.msg.chain.append(plain(sline(0,0,0),msg)) def add_candidate(self,sl,cand): cand=self.beautify(cand) self.msg.candidates.add(candidate(sl,cand)) def add_note(self,sline,msg): msg=self.beautify(msg) # print "ADDING NOTE %s" % msg self.start_new_entity_on_message() self.msg.chain.append(note(sline,msg)) self.msg.type="note" # XXX factor the both functions into almost one def add_error(self,sl,msg): rawmsg=msg msg=self.beautify(msg) # If there is already an error "in the buffer" then we can assume this # is a new one and flush the old one # print "add_error()" self.start_new_entity_on_message() self.msg.ew=error(sl,msg,rawmsg) self.msg.type="error" def add_warning(self,sl,msg,start_new=True): rawmsg=msg msg=self.beautify(msg) # print "add_warning()" # If there is already an error "in the buffer" then we can assume this # is a new one and flush the old one # print self.msg.ew if(start_new): self.start_new_entity_on_message() # print "add_warning_____" self.msg.ew=warning(sl,msg,rawmsg) self.msg.type="warning" def add_inclusion_trace(self,sl): # print("add_inclusion_trace") self.msg.inctrace.list.insert(0,include(sl)) def add_instantiation_trace(self,sl,msg): # print("add_instantiation_trace") msg=self.beautify(msg) self.msg.insttrace.list.insert(0,insttrace(sl,msg)) def add_expansion_trace(self,sl,msg): msg=self.beautify(msg) self.msg.exptrace.list.insert(0,exptrace(sl,msg)) def scan(self,line): line=line.rstrip("\n") # inclusion trace directives m=re.match(r"^In file included from (.*):([0-9]+)(:([0-9]+))*[,:]*$",line) if(m): # print("IN FILE INCLUDED") # print("self.special_single = '%s'" % self.special_single ) if( not self.special_single ): self.start_new_entity_on_message() self.add_inclusion_trace(sline(m.group(1),m.group(2),m.group(4))) return True m=re.match(r"^ from (.*):([0-9]+)(:([0-9]+))*[,:]*$",line) if(m): self.add_inclusion_trace(sline(m.group(1),m.group(2),m.group(4))) return True # instantiation traces m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* required from '(.*?)'$",line) if(m): self.add_instantiation_trace(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* instantiated from '(.*?)'$",line) if(m): self.add_instantiation_trace(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* instantiated from here$",line) if(m): self.add_instantiation_trace(sline(m.group(1),m.group(2),m.group(4)),None) return True ### XXX XXX this is a little bit differetn, just a hack for now m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* required from here$",line) if(m): self.add_instantiation_trace(sline(m.group(1),m.group(2),m.group(4)),None) return True m=re.match(r"^(.*?): In instantiation of '(.*?)':",line) if(m): self.start_new_entity_on_message() self.add_instantiation_trace(sline(m.group(1),None),m.group(2)) return True m=re.match(r"^(.*?): in definition of macro '(.*?)'",line) if(m): self.start_new_entity_on_message() self.add_expansion_trace(sline(m.group(1),None),m.group(2)) return True m=re.match(r"^(.*?): in expansion of macro '(.*?)'",line) if(m): self.add_expansion_trace(sline(m.group(1),None),m.group(2)) return True # scan first for location directives that tell us about the scope m=re.match(r"^(.*?): In function '(.*?)'+:$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"In function",m.group(2)) return True m=re.match(r"^(.*?): In lambda function:$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"In lambda function",None) return True m=re.match(r"^(.*?): In member function '(.*?)':$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"In member function",m.group(2)) return True m=re.match(r"^(.*?): In static member function '(.*?)':$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"In static member function",m.group(2)) return True m=re.match(r"^(.*?): In constructor '(.*?)':$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"In constructor",m.group(2)) return True m=re.match(r"^(.*?): At global scope:$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"At global scope",None) return True m=re.match(r"^(.*?): At top level:$",line) if(m): self.start_new_entity_on_message() self.add_location( sline(m.group(1),None),"At top level",None) return True # special cases for errors and warnings that have multiple lines m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: (conflicts with previous declaration .*)$",line) if(m): self.add_error_append(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: ( expected a template of type.*)$",line) if(m): self.add_error_append(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: (within this context)$",line) if(m): self.add_error_append(sline(m.group(1),m.group(2),m.group(4)),m.group(5),True) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: (provided for.*)$",line) if(m): self.add_error_append(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: ( initializing argument.*)$",line) if(m): self.add_error_append(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True # candidates filtering m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note: candidate is:(.*)",line) if(m): # print "ADDING CANDIDATE %s" % m.group(1) self.add_candidate(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note: candidates are:(.*)",line) if(m): self.add_candidate(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note: (.*)",line) if(m): self.add_candidate(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note:.*(no known conversion for.*)",line) if(m): self.add_candidate(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) self.next_is_candidate=True return True # generic warning/error fallbacks for standard errors or single line errors m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* warning: (.*)$",line) if(m): self.add_warning(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* error: (.*)$",line) if(m): self.add_error(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* fatal error: (.*)$",line) if(m): self.add_error(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) return True m=re.match(r"(compilation terminated.)",line) if(m): self.add_error_append(sline(None,None,None),m.group(1)) return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note: (suggested alternative:)$",line) if(m): self.start_new_entity_on_message() self.special_single=True self.add_note(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) # print("add_note SPECIAL") return True m=re.match(r"^(.*?):([0-9]+):(([0-9]+):)* note: (.*)$",line) if(m): if( self.next_is_candidate): self.next_is_candidate=False self.add_candidate(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) # print("added note as candidate") return True if( self.special_single ): self.start_new_entity_on_message() # self.special_single=True self.add_note(sline(m.group(1),m.group(2),m.group(4)),m.group(5)) # print("add_note_standard") return True # for certain linker errors we need a bit hackery since we do usually not have line numbers... m=re.match(r"^(.*?):(.*?): (undefined reference to '(.*?)')",line) if(m): # self.add_error(sline(m.group(1),None,m.group(2)),m.group(3)) self.add_undefined_reference(m.group(4)) return True # print line m=re.match(r"^\s+",line) if(m): self.add_code(line) return True self.add_unknown(line) return False def flush(self): if(not self.last_output): self.output_last_entity() self.output_undefined(self) if(self.suppressed_errors == 1): self.output("error","Suppressed one more error (all warnings shown)\n") elif(self.suppressed_errors > 0): self.output("error","Suppressed %s more errors (all warnings shown)\n" % self.suppressed_errors) def format(self,line): self.linecount+=1 # make sure that for single line outputs we do not do any formatting if( self.linecount == 1 ): self.firstline=line return if( self.linecount == 2 ): self.scan(self.firstline) self.scan(line) def run(self, gccout): for line in gccout: # replace all known possible entity delimiting characters by a # single tick, so we can much easier scan through the messages in a # uniform way line=line.replace("‘","'") line=line.replace("’","'") line=line.replace("`","'") self.format(line) if(self.linecount==1): r=self.scan(self.firstline) if(r): self.last_output=False else: sys.stdout.write(self.firstline) elif(self.linecount==0): # no error output at all return else: sys.stdout.write(self.colors.color("reset")) ########################### # # The global scope and "main" function # ########################### os.environ['LANG']='C' # sorry, but we work with english errors only os.environ['LC_ALL']='C' # sorry, but we work with english errors only c=config() f=formatter(c) p=None if( not os.isatty(0) and sys.argv[1] == "-R" ): f.run(sys.stdin) else: c.setup_compiler_command(sys.argv) args=[] args.append(c.cmd) args.extend(c.nargv) if( len(sys.argv) <= 1 or sys.argv[1] == "-v" ): p=subprocess.Popen(args,shell=False) else: p=subprocess.Popen(args,shell=False,bufsize=2048,stderr=subprocess.PIPE) f.run(p.stderr) f.flush() #for arg in args: # sys.stderr.write(arg + "\n") #output=p.communicate() #print output[0] #print >> sys.stderr, output[1] if( p ): p.wait() sys.exit(p.returncode) else: sys.exit(0) # vim: shiftwidth=4 tabstop=4 noexpandtab