Skip to content

Instantly share code, notes, and snippets.

@mooware mooware/colormsvc.py
Created Oct 13, 2017

Embed
What would you like to do?
Colorization for MSVC warnings and errors
import sys, re
COMPILE_PATTERN = re.compile(r'^.+\(\d+\) ?: (fatal )?(error|warning|note)( C\d+)?: .+$')
LINK_PATTERN = re.compile(r'^.+ : (fatal )?(error|warning) LNK\d+: .+$')
OTHER_PATTERN = re.compile(r'^.+ : (general |Command line )?(error|warning) \w+ ?: .+$')
class WinColorStream:
# wincon.h
FOREGROUND_BLACK = 0x0000
FOREGROUND_BLUE = 0x0001
FOREGROUND_GREEN = 0x0002
FOREGROUND_CYAN = 0x0003
FOREGROUND_RED = 0x0004
FOREGROUND_MAGENTA = 0x0005
FOREGROUND_YELLOW = 0x0006
FOREGROUND_GREY = 0x0007
FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.
FOREGROUND_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
BACKGROUND_BLACK = 0x0000
BACKGROUND_BLUE = 0x0010
BACKGROUND_GREEN = 0x0020
BACKGROUND_CYAN = 0x0030
BACKGROUND_RED = 0x0040
BACKGROUND_MAGENTA = 0x0050
BACKGROUND_YELLOW = 0x0060
BACKGROUND_GREY = 0x0070
BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
DEFAULT = FOREGROUND_WHITE
ERROR = FOREGROUND_RED | FOREGROUND_INTENSITY
WARNING = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
INFO = FOREGROUND_CYAN
def __init__(self, stream):
import ctypes, ctypes.util, ctypes.wintypes
self._stream = stream
# for some reason find_msvcrt() sometimes doesn't find msvcrt.dll on my system?
crtname = ctypes.util.find_msvcrt()
if not crtname:
crtname = ctypes.util.find_library('msvcrt')
crtlib = ctypes.cdll.LoadLibrary(crtname)
# get file handle for the stream
self._outhdl = crtlib._get_osfhandle(self._stream.fileno())
# get color setter function
self._set_console_text_attribute = ctypes.windll.kernel32.SetConsoleTextAttribute
self._set_console_text_attribute.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.WORD)
def _set_color(self, code):
self._set_console_text_attribute(self._outhdl, code)
def write(self, text, color):
self._set_color(color)
self._stream.write(text)
self._stream.flush()
self._set_color(self.DEFAULT)
def find_category(line):
match = COMPILE_PATTERN.match(line)
if not match:
match = LINK_PATTERN.match(line)
if not match:
match = OTHER_PATTERN.match(line)
if match:
return match.group(2)
def parse_int_arg(s):
"""Parse int to text with any base, i.e. supporting '0x' or '0' prefixes."""
return int(s, 0)
def parse_args(args):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--summary', action='store_true',
help='collect all errors and warnings and show a summary of them after compilation.')
parser.add_argument('--filter', action='store_true',
help='show only colored messages, i.e. errors, warnings and notes.')
parser.add_argument('--error', type=parse_int_arg, default=WinColorStream.ERROR,
help='set a custom color for error lines, see Win32 API SetConsoleTextAttribute() for possible values.')
parser.add_argument('--warning', type=parse_int_arg, default=WinColorStream.WARNING,
help='set a custom color for warning lines, see Win32 API SetConsoleTextAttribute() for possible values.')
parser.add_argument('--note', type=parse_int_arg, default=WinColorStream.INFO,
help='set a custom color for note lines, see Win32 API SetConsoleTextAttribute() for possible values.')
return parser.parse_args(args)
def main(args):
options = parse_args(args)
colors = { 'error': options.error, 'warning': options.warning, 'note': options.note }
stream = WinColorStream(sys.stdout)
warnings = []
errors = []
try:
while True:
line = sys.stdin.readline()
if not line:
break
category = find_category(line)
if options.filter and category is None:
continue
if options.summary:
if category == 'error':
errors.append(line)
elif category == 'warning':
warnings.append(line)
color = colors.get(category, WinColorStream.DEFAULT)
stream.write(line, color)
except KeyboardInterrupt:
pass # ctrl+c, stop
if errors or warnings:
stream.write('\nSummary:\n\n', WinColorStream.DEFAULT)
for line in warnings:
stream.write(line, options.warning)
for line in errors:
stream.write(line, options.error)
summary_color = options.error if errors else options.warning
stream.write('\n{} Warning{}, {} Error{}\n\n'.format(
len(warnings), '' if len(warnings) == 1 else 's',
len(errors), '' if len(errors) == 1 else 's'),
summary_color)
if __name__ == '__main__':
main(sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.