blob: 5b881ef8e28f6b4b3d2e75a138dd3bdec9827091 [file] [log] [blame]
# Copyright 2011 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
"""Simple color-enabled diagnositics reporting functions.
"""
import ctypes
import logging
import os
import sys
WINDOWS = sys.platform.startswith('win')
logger = logging.getLogger('diagnostics')
color_enabled = sys.stderr.isatty()
tool_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
# diagnostic levels
WARN = 1
ERROR = 2
FATAL = 3
# available colors
RED = 1
GREEN = 2
YELLOW = 3
BLUE = 4
MAGENTA = 5
CYAN = 6
WHITE = 7
# color for use for each diagnostic level
level_colors = {
WARN: MAGENTA,
ERROR: RED,
}
level_prefixes = {
WARN: 'warning: ',
ERROR: 'error: ',
}
# Constants from the Windows API
STD_OUTPUT_HANDLE = -11
def output_color_windows(color):
# TODO(sbc): This code is duplicated in colored_logger.py. Refactor.
# wincon.h
FOREGROUND_BLACK = 0x0000 # noqa
FOREGROUND_BLUE = 0x0001 # noqa
FOREGROUND_GREEN = 0x0002 # noqa
FOREGROUND_CYAN = 0x0003 # noqa
FOREGROUND_RED = 0x0004 # noqa
FOREGROUND_MAGENTA = 0x0005 # noqa
FOREGROUND_YELLOW = 0x0006 # noqa
FOREGROUND_GREY = 0x0007 # noqa
color_map = {
RED: FOREGROUND_RED,
GREEN: FOREGROUND_GREEN,
YELLOW: FOREGROUND_YELLOW,
BLUE: FOREGROUND_BLUE,
MAGENTA: FOREGROUND_MAGENTA,
CYAN: FOREGROUND_CYAN,
WHITE: FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
}
sys.stderr.flush()
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, color_map[color])
def get_color_windows():
SHORT = ctypes.c_short
WORD = ctypes.c_ushort
class COORD(ctypes.Structure):
_fields_ = [
("X", SHORT),
("Y", SHORT)]
class SMALL_RECT(ctypes.Structure):
_fields_ = [
("Left", SHORT),
("Top", SHORT),
("Right", SHORT),
("Bottom", SHORT)]
class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
_fields_ = [
("dwSize", COORD),
("dwCursorPosition", COORD),
("wAttributes", WORD),
("srWindow", SMALL_RECT),
("dwMaximumWindowSize", COORD)]
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
csbi = CONSOLE_SCREEN_BUFFER_INFO()
ctypes.windll.kernel32.GetConsoleScreenBufferInfo(hdl, ctypes.byref(csbi))
return csbi.wAttributes
def reset_color_windows():
sys.stderr.flush()
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, default_color)
def output_color(color):
if WINDOWS:
return output_color_windows(color)
return '\033[3%sm' % color
def reset_color():
if WINDOWS:
return reset_color_windows()
return '\033[0m'
def diag(level, msg, *args):
# Format output message as:
# <tool>: <level>: msg
# With the `<level>:` part being colored accordingly.
sys.stderr.write(tool_name + ': ')
if color_enabled:
output = output_color(level_colors[level])
if output:
sys.stderr.write(output)
sys.stderr.write(level_prefixes[level])
if color_enabled:
output = reset_color()
if output:
sys.stderr.write(output)
if args:
msg = msg % args
sys.stderr.write(str(msg))
sys.stderr.write('\n')
def error(msg, *args):
diag(ERROR, msg, *args)
sys.exit(1)
def warn(msg, *args):
diag(WARN, msg, *args)
class WarningManager:
warnings = {}
def add_warning(self, name, enabled=True, part_of_all=True, shared=False, error=False):
self.warnings[name] = {
'enabled': enabled,
'part_of_all': part_of_all,
# True for flags that are shared with the underlying clang driver
'shared': shared,
'error': error,
}
def capture_warnings(self, cmd_args):
for i in range(len(cmd_args)):
if cmd_args[i] == '-w':
for warning in self.warnings.values():
warning['enabled'] = False
continue
if not cmd_args[i].startswith('-W'):
continue
if cmd_args[i] == '-Wall':
for warning in self.warnings.values():
if warning['part_of_all']:
warning['enabled'] = True
continue
if cmd_args[i] == '-Werror':
for warning in self.warnings.values():
warning['error'] = True
continue
if cmd_args[i].startswith('-Werror=') or cmd_args[i].startswith('-Wno-error='):
warning_name = cmd_args[i].split('=', 1)[1]
if warning_name in self.warnings:
enabled = not cmd_args[i].startswith('-Wno-')
self.warnings[warning_name]['error'] = enabled
if enabled:
self.warnings[warning_name]['enabled'] = True
cmd_args[i] = ''
continue
warning_name = cmd_args[i].replace('-Wno-', '').replace('-W', '')
enabled = not cmd_args[i].startswith('-Wno-')
# special case pre-existing warn-absolute-paths
if warning_name == 'warn-absolute-paths':
self.warnings['absolute-paths']['enabled'] = enabled
cmd_args[i] = ''
continue
if warning_name in self.warnings:
self.warnings[warning_name]['enabled'] = enabled
if not self.warnings[warning_name]['shared']:
cmd_args[i] = ''
continue
return cmd_args
def warning(self, warning_type, message, *args):
warning_info = self.warnings[warning_type]
msg = (message % args) + ' [-W' + warning_type.lower().replace('_', '-') + ']'
if warning_info['enabled']:
if warning_info['error']:
error(msg + ' [-Werror]')
else:
warn(msg)
else:
logger.debug('disabled warning: ' + msg)
def add_warning(name, enabled=True, part_of_all=True, shared=False, error=False):
manager.add_warning(name, enabled, part_of_all, shared, error)
def enable_warning(name, as_error=False):
manager.warnings[name]['enabled'] = True
if as_error:
manager.warnings[name]['error'] = True
def disable_warning(name):
manager.warnings[name]['enabled'] = False
def warning(warning_type, message, *args):
manager.warning(warning_type, message, *args)
def capture_warnings(argv):
return manager.capture_warnings(argv)
if WINDOWS:
default_color = get_color_windows()
manager = WarningManager()