Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/diagnostics.py
4128 views
1
# Copyright 2011 The Emscripten Authors. All rights reserved.
2
# Emscripten is available under two separate licenses, the MIT license and the
3
# University of Illinois/NCSA Open Source License. Both these licenses can be
4
# found in the LICENSE file.
5
6
"""Simple color-enabled diagnositics reporting functions.
7
"""
8
9
import ctypes
10
import logging
11
import os
12
import sys
13
from typing import Dict
14
15
WINDOWS = sys.platform.startswith('win')
16
17
logger = logging.getLogger('diagnostics')
18
color_enabled = sys.stderr.isatty()
19
tool_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
20
force_ansi = False
21
22
# diagnostic levels
23
WARN = 1
24
ERROR = 2
25
FATAL = 3
26
27
# available colors
28
RED = 1
29
GREEN = 2
30
YELLOW = 3
31
BLUE = 4
32
MAGENTA = 5
33
CYAN = 6
34
WHITE = 7
35
36
# color for use for each diagnostic level
37
level_colors = {
38
WARN: MAGENTA,
39
ERROR: RED,
40
}
41
42
level_prefixes = {
43
WARN: 'warning: ',
44
ERROR: 'error: ',
45
}
46
47
# Constants from the Windows API
48
STD_OUTPUT_HANDLE = -11
49
50
51
def output_color_windows(color):
52
assert not force_ansi
53
# TODO(sbc): This code is duplicated in colored_logger.py. Refactor.
54
# wincon.h
55
FOREGROUND_BLACK = 0x0000 # noqa
56
FOREGROUND_BLUE = 0x0001 # noqa
57
FOREGROUND_GREEN = 0x0002 # noqa
58
FOREGROUND_CYAN = 0x0003 # noqa
59
FOREGROUND_RED = 0x0004 # noqa
60
FOREGROUND_MAGENTA = 0x0005 # noqa
61
FOREGROUND_YELLOW = 0x0006 # noqa
62
FOREGROUND_GREY = 0x0007 # noqa
63
64
color_map = {
65
RED: FOREGROUND_RED,
66
GREEN: FOREGROUND_GREEN,
67
YELLOW: FOREGROUND_YELLOW,
68
BLUE: FOREGROUND_BLUE,
69
MAGENTA: FOREGROUND_MAGENTA,
70
CYAN: FOREGROUND_CYAN,
71
WHITE: FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED,
72
}
73
74
sys.stderr.flush()
75
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
76
ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, color_map[color])
77
78
79
def get_color_windows():
80
assert not force_ansi
81
SHORT = ctypes.c_short
82
WORD = ctypes.c_ushort
83
84
class COORD(ctypes.Structure):
85
_fields_ = [
86
("X", SHORT),
87
("Y", SHORT)]
88
89
class SMALL_RECT(ctypes.Structure):
90
_fields_ = [
91
("Left", SHORT),
92
("Top", SHORT),
93
("Right", SHORT),
94
("Bottom", SHORT)]
95
96
class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
97
_fields_ = [
98
("dwSize", COORD),
99
("dwCursorPosition", COORD),
100
("wAttributes", WORD),
101
("srWindow", SMALL_RECT),
102
("dwMaximumWindowSize", COORD)]
103
104
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
105
csbi = CONSOLE_SCREEN_BUFFER_INFO()
106
ctypes.windll.kernel32.GetConsoleScreenBufferInfo(hdl, ctypes.byref(csbi))
107
return csbi.wAttributes
108
109
110
def reset_color_windows():
111
assert not force_ansi
112
sys.stderr.flush()
113
hdl = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
114
ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, default_color)
115
116
117
def output_color(color):
118
if WINDOWS and not force_ansi:
119
output_color_windows(color)
120
return ''
121
return '\033[3%sm' % color
122
123
124
def bold():
125
if WINDOWS and not force_ansi:
126
# AFAICT there is no way to enable bold output on windows
127
return ''
128
return '\033[1m'
129
130
131
def reset_color():
132
if WINDOWS and not force_ansi:
133
reset_color_windows()
134
return ''
135
return '\033[0m'
136
137
138
def diag(level, msg, *args):
139
# Format output message as:
140
# <tool>: <level>: msg
141
# With the `<level>:` part being colored accordingly.
142
sys.stderr.write(tool_name + ': ')
143
144
if color_enabled:
145
output = output_color(level_colors[level]) + bold()
146
if output:
147
sys.stderr.write(output)
148
149
sys.stderr.write(level_prefixes[level])
150
151
if color_enabled:
152
output = reset_color() + bold()
153
if output:
154
sys.stderr.write(output)
155
156
if args:
157
msg = msg % args
158
sys.stderr.write(str(msg))
159
sys.stderr.write('\n')
160
161
if color_enabled:
162
output = reset_color()
163
if output:
164
sys.stderr.write(output)
165
166
167
def error(msg, *args):
168
diag(ERROR, msg, *args)
169
sys.exit(1)
170
171
172
def warn(msg, *args):
173
diag(WARN, msg, *args)
174
175
176
class WarningManager:
177
warnings: Dict[str, Dict] = {}
178
179
def add_warning(self, name, enabled=True, part_of_all=True, shared=False, error=False):
180
self.warnings[name] = {
181
'enabled': enabled,
182
'part_of_all': part_of_all,
183
# True for flags that are shared with the underlying clang driver
184
'shared': shared,
185
'error': error,
186
}
187
188
def capture_warnings(self, cmd_args):
189
for i in range(len(cmd_args)):
190
if cmd_args[i] == '-w':
191
for warning in self.warnings.values():
192
warning['enabled'] = False
193
continue
194
195
if not cmd_args[i].startswith('-W'):
196
continue
197
198
if cmd_args[i] == '-Wall':
199
for warning in self.warnings.values():
200
if warning['part_of_all']:
201
warning['enabled'] = True
202
continue
203
204
if cmd_args[i] == '-Werror':
205
for warning in self.warnings.values():
206
warning['error'] = True
207
continue
208
209
if cmd_args[i].startswith('-Werror=') or cmd_args[i].startswith('-Wno-error='):
210
warning_name = cmd_args[i].split('=', 1)[1]
211
if warning_name in self.warnings:
212
enabled = not cmd_args[i].startswith('-Wno-')
213
self.warnings[warning_name]['error'] = enabled
214
if enabled:
215
self.warnings[warning_name]['enabled'] = True
216
cmd_args[i] = ''
217
continue
218
219
warning_name = cmd_args[i].replace('-Wno-', '').replace('-W', '')
220
enabled = not cmd_args[i].startswith('-Wno-')
221
222
# special case pre-existing warn-absolute-paths
223
if warning_name == 'warn-absolute-paths':
224
self.warnings['absolute-paths']['enabled'] = enabled
225
cmd_args[i] = ''
226
continue
227
228
if warning_name in self.warnings:
229
self.warnings[warning_name]['enabled'] = enabled
230
if not self.warnings[warning_name]['shared']:
231
cmd_args[i] = ''
232
continue
233
234
return cmd_args
235
236
def warning(self, warning_type, message, *args):
237
warning_info = self.warnings[warning_type]
238
msg = (message % args) + ' [-W' + warning_type.lower().replace('_', '-') + ']'
239
if warning_info['enabled']:
240
if warning_info['error']:
241
error(msg + ' [-Werror]')
242
else:
243
warn(msg)
244
else:
245
logger.debug('disabled warning: ' + msg)
246
247
248
def add_warning(name, enabled=True, part_of_all=True, shared=False, error=False):
249
manager.add_warning(name, enabled, part_of_all, shared, error)
250
251
252
def enable_warning(name, as_error=False):
253
manager.warnings[name]['enabled'] = True
254
if as_error:
255
manager.warnings[name]['error'] = True
256
257
258
def disable_warning(name):
259
manager.warnings[name]['enabled'] = False
260
261
262
def is_enabled(name):
263
return manager.warnings[name]['enabled']
264
265
266
def warning(warning_type, message, *args):
267
manager.warning(warning_type, message, *args)
268
269
270
def capture_warnings(argv):
271
return manager.capture_warnings(argv)
272
273
274
if WINDOWS:
275
default_color = get_color_windows()
276
277
manager = WarningManager()
278
279