Skip to content

Commit 1989b1b

Browse files
committed
SCons: Add silence_msvc option
1 parent c414c2b commit 1989b1b

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

tools/windows.py

+69
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,76 @@
55
from SCons.Variables import *
66

77

8+
def silence_msvc(env):
9+
import os
10+
import re
11+
import tempfile
12+
13+
# Ensure we have a location to write captured output to, in case of false positives.
14+
capture_path = os.path.join(os.path.dirname(__file__), "..", "msvc_capture.log")
15+
with open(capture_path, "wt", encoding="utf-8"):
16+
pass
17+
18+
old_spawn = env["SPAWN"]
19+
re_redirect_stream = re.compile(r"^[12]?>")
20+
re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE)
21+
re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)')
22+
23+
def spawn_capture(sh, escape, cmd, args, env):
24+
# We only care about cl/link, process everything else as normal.
25+
if args[0] not in ["cl", "link"]:
26+
return old_spawn(sh, escape, cmd, args, env)
27+
28+
# Process as normal if the user is manually rerouting output.
29+
for arg in args:
30+
if re_redirect_stream.match(arg):
31+
return old_spawn(sh, escape, cmd, args, env)
32+
33+
tmp_stdout, tmp_stdout_name = tempfile.mkstemp()
34+
os.close(tmp_stdout)
35+
args.append(f">{tmp_stdout_name}")
36+
ret = old_spawn(sh, escape, cmd, args, env)
37+
38+
try:
39+
with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout:
40+
lines = tmp_stdout.read().splitlines()
41+
os.remove(tmp_stdout_name)
42+
except OSError:
43+
pass
44+
45+
# Early process no lines (OSError)
46+
if not lines:
47+
return ret
48+
49+
is_cl = args[0] == "cl"
50+
content = ""
51+
caught = False
52+
for line in lines:
53+
# These conditions are far from all-encompassing, but are specialized
54+
# for what can be reasonably expected to show up in the repository.
55+
if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)):
56+
caught = True
57+
try:
58+
with open(capture_path, "a", encoding=sys.stdout.encoding) as log:
59+
log.write(line + "\n")
60+
except OSError:
61+
print(f'WARNING: Failed to log captured line: "{line}".')
62+
continue
63+
content += line + "\n"
64+
# Content remaining assumed to be an error/warning.
65+
if content:
66+
sys.stderr.write(content)
67+
68+
return ret
69+
70+
env["SPAWN"] = spawn_capture
71+
72+
873
def options(opts):
974
opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False))
1075
opts.Add(BoolVariable("use_clang_cl", "Use the clang driver instead of MSVC - only effective on Windows", False))
1176
opts.Add(BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True))
77+
opts.Add(BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting errors to stderr.", True))
1278

1379

1480
def exists(env):
@@ -42,6 +108,9 @@ def generate(env):
42108
else:
43109
env.Append(CCFLAGS=["/MD"])
44110

111+
if env["silence_msvc"] and not env.GetOption("clean"):
112+
silence_msvc(env)
113+
45114
elif sys.platform == "win32" or sys.platform == "msys":
46115
env["use_mingw"] = True
47116
mingw.generate(env)

0 commit comments

Comments
 (0)