mirror of
https://chromium.googlesource.com/chromium/tools/depot_tools.git
synced 2026-01-11 18:51:29 +00:00
gclient: Preserve ANSI color codes when calling hooks.
If a hook prints error/warning output that's color-coded, gclient will cause the coloring to be disabled since those hooks aren't called directly from a terminal. By emulating a terminal when launching subprocs from gclient, we can convince them to keep the color escape codes. LED builds with both //third_party/depot_tools rolled to this CL, as well as depot_tools in the recipe bundle rolled to this CL: linux: https://ci.chromium.org/swarming/task/4e40237985888310 mac: https://ci.chromium.org/swarming/task/4e4023ea0c829710 win: https://ci.chromium.org/swarming/task/4e4024612e03dc10 Bug: 1034063 Change-Id: I4150f66ef215ece06f4c32482dcd4ded14eb1bfe Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2368435 Reviewed-by: Dirk Pranke <dpranke@google.com> Commit-Queue: Ben Pastene <bpastene@chromium.org>
This commit is contained in:
@@ -10,6 +10,7 @@ import codecs
|
||||
import collections
|
||||
import contextlib
|
||||
import datetime
|
||||
import errno
|
||||
import functools
|
||||
import io
|
||||
import logging
|
||||
@@ -585,9 +586,19 @@ def CheckCallAndFilter(args, print_stdout=False, filter_fn=None,
|
||||
sleep_interval = RETRY_INITIAL_SLEEP
|
||||
run_cwd = kwargs.get('cwd', os.getcwd())
|
||||
for attempt in range(RETRY_MAX + 1):
|
||||
# If our stdout is a terminal, then pass in a psuedo-tty pipe to our
|
||||
# subprocess when filtering its output. This makes the subproc believe
|
||||
# it was launched from a terminal, which will preserve ANSI color codes.
|
||||
if sys.stdout.isatty():
|
||||
pipe_reader, pipe_writer = os.openpty()
|
||||
else:
|
||||
pipe_reader, pipe_writer = os.pipe()
|
||||
|
||||
kid = subprocess2.Popen(
|
||||
args, bufsize=0, stdout=subprocess2.PIPE, stderr=subprocess2.STDOUT,
|
||||
args, bufsize=0, stdout=pipe_writer, stderr=subprocess2.STDOUT,
|
||||
**kwargs)
|
||||
# Close the write end of the pipe once we hand it off to the child proc.
|
||||
os.close(pipe_writer)
|
||||
|
||||
GClientChildren.add(kid)
|
||||
|
||||
@@ -608,7 +619,14 @@ def CheckCallAndFilter(args, print_stdout=False, filter_fn=None,
|
||||
try:
|
||||
line_start = None
|
||||
while True:
|
||||
in_byte = kid.stdout.read(1)
|
||||
try:
|
||||
in_byte = os.read(pipe_reader, 1)
|
||||
except (IOError, OSError) as e:
|
||||
if e.errno == errno.EIO:
|
||||
# An errno.EIO means EOF?
|
||||
in_byte = None
|
||||
else:
|
||||
raise e
|
||||
is_newline = in_byte in (b'\n', b'\r')
|
||||
if not in_byte:
|
||||
break
|
||||
@@ -629,8 +647,8 @@ def CheckCallAndFilter(args, print_stdout=False, filter_fn=None,
|
||||
if line_start is not None:
|
||||
filter_line(command_output, line_start)
|
||||
|
||||
os.close(pipe_reader)
|
||||
rv = kid.wait()
|
||||
kid.stdout.close()
|
||||
|
||||
# Don't put this in a 'finally,' since the child may still run if we get
|
||||
# an exception.
|
||||
|
||||
Reference in New Issue
Block a user