Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1548)

Unified Diff: gclient_utils.py

Issue 227163002: Revamped terminal output for update. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: rebase Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gclient_scm.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gclient_utils.py
diff --git a/gclient_utils.py b/gclient_utils.py
index 3517946435a6efe346af97bb539f15f46f624166..f89b601f3a201146e8446aee570cd3bc30f7762a 100644
--- a/gclient_utils.py
+++ b/gclient_utils.py
@@ -6,6 +6,7 @@
import codecs
import cStringIO
+import datetime
import logging
import os
import pipes
@@ -25,6 +26,7 @@ import subprocess2
RETRY_MAX = 3
RETRY_INITIAL_SLEEP = 0.5
+START = datetime.datetime.now()
_WARNINGS = []
@@ -47,6 +49,12 @@ class Error(Exception):
super(Error, self).__init__(msg, *args, **kwargs)
+def Elapsed(until=None):
+ if until is None:
+ until = datetime.datetime.now()
+ return str(until - START).partition('.')[0]
+
+
def PrintWarnings():
"""Prints any accumulated warnings."""
if _WARNINGS:
@@ -483,12 +491,8 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
output.write(in_byte)
if print_stdout:
stdout.write(in_byte)
- if in_byte != '\r':
- if in_byte != '\n':
- in_line += in_byte
- else:
- filter_fn(in_line)
- in_line = ''
+ if in_byte not in ['\r', '\n']:
+ in_line += in_byte
else:
filter_fn(in_line)
in_line = ''
@@ -525,9 +529,9 @@ class GitFilter(object):
Allows a custom function to skip certain lines (predicate), and will throttle
the output of percentage completed lines to only output every X seconds.
"""
- PERCENT_RE = re.compile('.* ([0-9]{1,2})% .*')
+ PERCENT_RE = re.compile('(.*) ([0-9]{1,3})% .*')
- def __init__(self, time_throttle=0, predicate=None):
+ def __init__(self, time_throttle=0, predicate=None, out_fh=None):
"""
Args:
time_throttle (int): GitFilter will throttle 'noisy' output (such as the
@@ -535,10 +539,13 @@ class GitFilter(object):
seconds apart.
predicate (f(line)): An optional function which is invoked for every line.
The line will be skipped if predicate(line) returns False.
+ out_fh: File handle to write output to.
"""
self.last_time = 0
self.time_throttle = time_throttle
self.predicate = predicate
+ self.out_fh = out_fh or sys.stdout
+ self.progress_prefix = None
def __call__(self, line):
# git uses an escape sequence to clear the line; elide it.
@@ -549,11 +556,14 @@ class GitFilter(object):
return
now = time.time()
match = self.PERCENT_RE.match(line)
- if not match:
- self.last_time = 0
- if (now - self.last_time) >= self.time_throttle:
- self.last_time = now
- print line
+ if match:
+ if match.group(1) != self.progress_prefix:
+ self.progress_prefix = match.group(1)
+ elif now - self.last_time < self.time_throttle:
+ return
+ self.last_time = now
+ self.out_fh.write('[%s] ' % Elapsed())
+ print >> self.out_fh, line
def FindGclientRoot(from_dir, filename='.gclient'):
@@ -683,6 +693,8 @@ class WorkItem(object):
def __init__(self, name):
# A unique string representing this work item.
self._name = name
+ self.outbuf = cStringIO.StringIO()
+ self.start = self.finish = None
def run(self, work_queue):
"""work_queue is passed as keyword argument so it should be
@@ -704,7 +716,7 @@ class ExecutionQueue(object):
Methods of this class are thread safe.
"""
- def __init__(self, jobs, progress, ignore_requirements):
+ def __init__(self, jobs, progress, ignore_requirements, verbose=False):
"""jobs specifies the number of concurrent tasks to allow. progress is a
Progress instance."""
# Set when a thread is done or a new item is enqueued.
@@ -725,6 +737,9 @@ class ExecutionQueue(object):
self.progress.update(0)
self.ignore_requirements = ignore_requirements
+ self.verbose = verbose
+ self.last_join = None
+ self.last_subproc_output = None
def enqueue(self, d):
"""Enqueue one Dependency to be executed later once its requirements are
@@ -743,9 +758,30 @@ class ExecutionQueue(object):
finally:
self.ready_cond.release()
+ def out_cb(self, _):
+ self.last_subproc_output = datetime.datetime.now()
+ return True
+
+ @staticmethod
+ def format_task_output(task, comment=''):
+ if comment:
+ comment = ' (%s)' % comment
+ if task.start and task.finish:
+ elapsed = ' (Elapsed: %s)' % (
+ str(task.finish - task.start).partition('.')[0])
+ else:
+ elapsed = ''
+ return """
+%s%s%s
+----------------------------------------
+%s
+----------------------------------------""" % (
+ task.name, comment, task.outbuf.getvalue().strip(), elapsed)
+
def flush(self, *args, **kwargs):
"""Runs all enqueued items until all are executed."""
kwargs['work_queue'] = self
+ self.last_subproc_output = self.last_join = datetime.datetime.now()
self.ready_cond.acquire()
try:
while True:
@@ -778,6 +814,17 @@ class ExecutionQueue(object):
# We need to poll here otherwise Ctrl-C isn't processed.
try:
self.ready_cond.wait(10)
+ # If we haven't printed to terminal for a while, but we have received
+ # spew from a suprocess, let the user know we're still progressing.
+ now = datetime.datetime.now()
+ if (now - self.last_join > datetime.timedelta(seconds=60) and
+ self.last_subproc_output > self.last_join):
+ if self.progress:
+ print >> sys.stdout, ''
+ elapsed = Elapsed()
+ print >> sys.stdout, '[%s] Still working on:' % elapsed
+ for task in self.running:
+ print >> sys.stdout, '[%s] %s' % (elapsed, task.item.name)
except KeyboardInterrupt:
# Help debugging by printing some information:
print >> sys.stderr, (
@@ -788,7 +835,10 @@ class ExecutionQueue(object):
', '.join(self.ran),
len(self.running)))
for i in self.queued:
- print >> sys.stderr, '%s: %s' % (i.name, ', '.join(i.requirements))
+ print >> sys.stderr, '%s (not started): %s' % (
+ i.name, ', '.join(i.requirements))
+ for i in self.running:
+ print >> sys.stderr, self.format_task_output(i.item, 'interrupted')
raise
# Something happened: self.enqueue() or a thread terminated. Loop again.
finally:
@@ -796,11 +846,14 @@ class ExecutionQueue(object):
assert not self.running, 'Now guaranteed to be single-threaded'
if not self.exceptions.empty():
+ if self.progress:
+ print >> sys.stdout, ''
# To get back the stack location correctly, the raise a, b, c form must be
# used, passing a tuple as the first argument doesn't work.
- e = self.exceptions.get()
+ e, task = self.exceptions.get()
+ print >> sys.stderr, self.format_task_output(task.item, 'ERROR')
raise e[0], e[1], e[2]
- if self.progress:
+ elif self.progress:
self.progress.end()
def _flush_terminated_threads(self):
@@ -812,7 +865,10 @@ class ExecutionQueue(object):
self.running.append(t)
else:
t.join()
+ self.last_join = datetime.datetime.now()
sys.stdout.flush()
+ if self.verbose:
+ print >> sys.stdout, self.format_task_output(t.item)
if self.progress:
self.progress.update(1, t.item.name)
if t.item.name in self.ran:
@@ -832,10 +888,26 @@ class ExecutionQueue(object):
else:
# Run the 'thread' inside the main thread. Don't try to catch any
# exception.
- task_item.run(*args, **kwargs)
- self.ran.append(task_item.name)
- if self.progress:
- self.progress.update(1, ', '.join(t.item.name for t in self.running))
+ try:
+ task_item.start = datetime.datetime.now()
+ print >> task_item.outbuf, '[%s] Started.' % Elapsed(task_item.start)
+ task_item.run(*args, **kwargs)
+ task_item.finish = datetime.datetime.now()
+ print >> task_item.outbuf, '[%s] Finished.' % Elapsed(task_item.finish)
+ self.ran.append(task_item.name)
+ if self.verbose:
+ if self.progress:
+ print >> sys.stdout, ''
+ print >> sys.stdout, self.format_task_output(task_item)
+ if self.progress:
+ self.progress.update(1, ', '.join(t.item.name for t in self.running))
+ except KeyboardInterrupt:
+ print >> sys.stderr, self.format_task_output(task_item, 'interrupted')
+ raise
+ except Exception:
+ print >> sys.stderr, self.format_task_output(task_item, 'ERROR')
+ raise
+
class _Worker(threading.Thread):
"""One thread to execute one WorkItem."""
@@ -853,17 +925,21 @@ class ExecutionQueue(object):
logging.debug('_Worker.run(%s)' % self.item.name)
work_queue = self.kwargs['work_queue']
try:
+ self.item.start = datetime.datetime.now()
+ print >> self.item.outbuf, '[%s] Started.' % Elapsed(self.item.start)
self.item.run(*self.args, **self.kwargs)
+ self.item.finish = datetime.datetime.now()
+ print >> self.item.outbuf, '[%s] Finished.' % Elapsed(self.item.finish)
except KeyboardInterrupt:
logging.info('Caught KeyboardInterrupt in thread %s', self.item.name)
logging.info(str(sys.exc_info()))
- work_queue.exceptions.put(sys.exc_info())
+ work_queue.exceptions.put((sys.exc_info(), self))
raise
except Exception:
# Catch exception location.
logging.info('Caught exception in thread %s', self.item.name)
logging.info(str(sys.exc_info()))
- work_queue.exceptions.put(sys.exc_info())
+ work_queue.exceptions.put((sys.exc_info(), self))
finally:
logging.info('_Worker.run(%s) done', self.item.name)
work_queue.ready_cond.acquire()
« no previous file with comments | « gclient_scm.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698