#!/usr/bin/env python # -*- coding: iso-8859-1 -*- #----------------------------------------------------------------------------- # Copyright (c) 2008-2009 Fabrice HARROUET (ENIB) # # Permission to use, copy, modify, distribute and sell this software # and its documentation for any purpose is hereby granted without fee, # provided that the above copyright notice appear in all copies and # that both that copyright notice and this permission notice appear # in supporting documentation. # The author makes no representations about the suitability of this # software for any purpose. # It is provided "as is" without express or implied warranty. #----------------------------------------------------------------------------- ''' This Python script is invoked as a compiler and colorizes the messages generated by your system compiler. The main purpose is inspired from the no longer maintained colorgcc Perl script but this Python script goes a bit further. Supported compilers are gcc, g++, icc and icpc under UNIX cl.exe and icl.exe under Window$ For an explicit usage, simply do: color_cc.py mycompiler options ... For an implicit usage under UNIX: ln -s color_cc.py mycompiler You must ensure this symlink is found in your PATH _before_ the true compiler executable, so that invoking mycompiler options ... executes this Python script. There is no implicit usage under Window$ since there are no symlinks :-( A workaround consists in renaming this script as mycompiler.py. Then invoking mycompiler.py options ... will look for mycompiler.exe and colorize its messages. ''' import sys import os import subprocess import re import platform resetColor ='\x1b[0m' blackColor ='\x1b[0;30m' boldBlackColor ='\x1b[1;30m' redColor ='\x1b[0;31m' boldRedColor ='\x1b[1;31m' greenColor ='\x1b[0;32m' boldGreenColor ='\x1b[1;32m' yellowColor ='\x1b[0;33m' boldYellowColor ='\x1b[1;33m' blueColor ='\x1b[0;34m' boldBlueColor ='\x1b[1;34m' magentaColor ='\x1b[0;35m' boldMagentaColor='\x1b[1;35m' cyanColor ='\x1b[0;36m' boldCyanColor ='\x1b[1;36m' whiteColor ='\x1b[0;37m' boldWhiteColor ='\x1b[1;37m' quoteColor=greenColor errorColor=boldRedColor warningColor=yellowColor infoColor=boldBlueColor if platform.system()=='Windows': msgOutput=sys.stdout else: msgOutput=sys.stderr compiler=None thisScript=os.path.basename(os.path.realpath(sys.argv[0])) cmd=os.path.basename(sys.argv[0]) if cmd==thisScript: sys.argv=sys.argv[1:] cmd=os.path.basename(sys.argv[0]) sp=os.path.splitext(cmd) if sp[1]=='.py': cmd=sp[0]+'.exe' for i in [i or os.path.curdir for i in os.environ['PATH'].split(os.path.pathsep)]: i=os.path.join(i,cmd) if os.path.isfile(i) and os.access(i,os.X_OK) and \ os.path.basename(os.path.realpath(i))!=thisScript: compiler=i break if not compiler: sys.stderr.write('Cannot find any `%s\' compiler different from `%s\'\n' % (cmd,thisScript)) sys.exit(1) cmdLine=[compiler]+sys.argv[1:] # print cmdLine if not os.isatty(msgOutput.fileno()): os.execv(compiler,cmdLine) sys.exit(127) if platform.system()=='Windows': import ctypes import struct handle=ctypes.windll.kernel32.GetStdHandle(-11) # stdout attribs=ctypes.create_string_buffer(22) ctypes.windll.kernel32.GetConsoleScreenBufferInfo(handle,attribs) (szx,szy,posx,posy,consAttr,winl,wint,winr,winb,maxszx,maxszy)= \ struct.unpack("hhhhHhhhhhh",attribs.raw) colors=[(resetColor ,consAttr), (blackColor ,0x00) ,(boldBlackColor ,0x08), (redColor ,0x04) ,(boldRedColor ,0x0C), (greenColor ,0x02) ,(boldGreenColor ,0x0A), (yellowColor ,0x06) ,(boldYellowColor ,0x0E), (blueColor ,0x01) ,(boldBlueColor ,0x09), (magentaColor,0x05) ,(boldMagentaColor,0x0D), (cyanColor ,0x03) ,(boldCyanColor ,0x0B), (whiteColor ,0x07) ,(boldWhiteColor ,0x0F)] def consoleWrite(l): while True: pos=l.find('\x1b[') if pos==-1: msgOutput.write(l) break msgOutput.write(l[0:pos]) l=l[pos:] for (c,a) in colors: if l.startswith(c): ctypes.windll.kernel32.SetConsoleTextAttribute(handle,a) l=l[len(c):] break else: def consoleWrite(l): msgOutput.write(l) if cmd.find('gcc')!=-1 or cmd.find('g++')!=-1: errorRe=re.compile(r'^([^:]*:)([0-9]+(:[0-9]+)?)([:,].*)$') infoRe=re.compile(r'^([^:]*)(:.*:)$') def colorize(l): global currentColor m=errorRe.match(l) if m: if len(m.group(4))==1: currentColor=infoColor elif m.group(4).find('err')!=-1: currentColor=errorColor else: currentColor=warningColor l=m.group(1) l+=quoteColor l+=m.group(2) l+=currentColor l+=m.group(4) else: m=infoRe.match(l) if m: currentColor=infoColor return l elif cmd.find('icc')!=-1 or cmd.find('icpc')!=-1 or cmd.find('icl.exe')!=-1: errorRe1=re.compile(r'^(.*\()([0-9]+)(\):([^:]+):.*)$') errorRe2=re.compile(r'^(.*)(:([^:]+):.*)$') def colorize(l): global currentColor m=errorRe1.match(l) if m: if m.group(4).find('err')!=-1: currentColor=errorColor else: currentColor=warningColor l=m.group(1) l+=quoteColor l+=m.group(2) l+=currentColor l+=m.group(3) else: m=errorRe2.match(l) if m: if m.group(3).find('err')!=-1: currentColor=errorColor else: currentColor=warningColor return l elif cmd.find('cl.exe')!=-1: errorRe1=re.compile(r'^(.*\()([0-9]+)(\) :([^:]+):.*)$') errorRe2=re.compile(r'^(.*)( :([^:]+):.*)$') def colorize(l): global currentColor m=errorRe1.match(l) if m: if m.group(4).find('err')!=-1: currentColor=errorColor else: currentColor=warningColor l=m.group(1) l+=quoteColor l+=m.group(2) l+=currentColor l+=m.group(3) else: m=errorRe2.match(l) if m: if m.group(3).find('err')!=-1: currentColor=errorColor else: currentColor=warningColor return l else: def colorize(l): global currentColor return l if msgOutput is sys.stdout: proc=subprocess.Popen(cmdLine,stdout=subprocess.PIPE) msgInput=proc.stdout else: proc=subprocess.Popen(cmdLine,stderr=subprocess.PIPE) msgInput=proc.stderr quoteRe=re.compile("(«[^«»]+»|`[^`']+'|'[^']+'|\"[^\"]+\")") currentColor=resetColor+infoColor for l in msgInput: if l.endswith('\n'): l=l[:-1] if l: # print l l=colorize(l) l=re.sub(quoteRe,r'%s\1%s'%(quoteColor,currentColor),l) else: currentColor=resetColor+infoColor consoleWrite(currentColor+l+resetColor+'\n') proc.wait() sys.exit(proc.returncode) #-----------------------------------------------------------------------------