# 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: # : : msg # With the `:` 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()