From 0ec4ec0f90c3682d0b17dff704a615a02604f6e6 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Sun, 12 May 2024 18:10:22 -0500 Subject: [PATCH] SCons: Fix `silence_msvc` implementation errors --- platform/windows/detect.py | 49 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 93eb34001e9..86e080dabc2 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,5 +1,6 @@ import methods import os +import re import subprocess import sys from methods import print_warning, print_error @@ -304,6 +305,7 @@ def setup_msvc_manual(env: "SConsEnvironment"): print("Using VCVARS-determined MSVC, arch %s" % (env_arch)) +# FIXME: Likely overwrites command-line options for the msvc compiler. See #91883. def setup_msvc_auto(env: "SConsEnvironment"): """Set up MSVC using SCons's auto-detection logic""" @@ -386,33 +388,64 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable. - if env["silence_msvc"]: + if env["silence_msvc"] and not env.GetOption("clean"): from tempfile import mkstemp + # Ensure we have a location to write captured output to, in case of false positives. + capture_path = methods.base_folder_path + "platform/windows/msvc_capture.log" + with open(capture_path, "wt"): + pass + old_spawn = env["SPAWN"] + re_redirect_stream = re.compile(r"^[12]?>") + re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE) + re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)') def spawn_capture(sh, escape, cmd, args, env): # We only care about cl/link, process everything else as normal. if args[0] not in ["cl", "link"]: return old_spawn(sh, escape, cmd, args, env) + # Process as normal if the user is manually rerouting output. + for arg in args: + if re_redirect_stream.match(arg): + return old_spawn(sh, escape, cmd, args, env) + tmp_stdout, tmp_stdout_name = mkstemp() os.close(tmp_stdout) args.append(f">{tmp_stdout_name}") ret = old_spawn(sh, escape, cmd, args, env) try: - with open(tmp_stdout_name, "rb") as tmp_stdout: - # First line is always bloat, subsequent lines are always errors. If content - # exists after discarding the first line, safely decode & send to stderr. - tmp_stdout.readline() - content = tmp_stdout.read() - if content: - sys.stderr.write(content.decode(sys.stdout.encoding, "replace")) + with open(tmp_stdout_name, encoding="oem", errors="replace") as tmp_stdout: + lines = tmp_stdout.read().splitlines() os.remove(tmp_stdout_name) except OSError: pass + # Early process no lines (OSError) + if not lines: + return ret + + is_cl = args[0] == "cl" + content = "" + caught = False + for line in lines: + # These conditions are far from all-encompassing, but are specialized + # for what can be reasonably expected to show up in the repository. + if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)): + caught = True + try: + with open(capture_path, "a") as log: + log.write(line + "\n") + except OSError: + print_warning(f'Failed to log captured line: "{line}".') + continue + content += line + "\n" + # Content remaining assumed to be an error/warning. + if content: + sys.stderr.write(content) + return ret env["SPAWN"] = spawn_capture