summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitbake/lib/bb/ui/knotty.py20
-rw-r--r--bitbake/lib/progressbar.py384
2 files changed, 396 insertions, 8 deletions
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py
index 177a12609..a34991bb6 100644
--- a/bitbake/lib/bb/ui/knotty.py
+++ b/bitbake/lib/bb/ui/knotty.py
@@ -25,11 +25,13 @@ import sys
import itertools
import xmlrpclib
import logging
+import progressbar
from bb import ui
from bb.ui import uihelper
logger = logging.getLogger("BitBake")
-parsespin = itertools.cycle( r'|/-\\' )
+widgets = ['Parsing recipes: ', progressbar.Percentage(), ' ',
+ progressbar.Bar(), ' ', progressbar.ETA()]
class BBLogFormatter(logging.Formatter):
"""Formatter which ensures that our 'plain' messages (logging.INFO + 1) are used as is"""
@@ -75,6 +77,7 @@ def init(server, eventHandler):
print("XMLRPC Fault getting commandline:\n %s" % x)
return 1
+ pbar = None
shutdown = 0
return_value = 0
while True:
@@ -130,19 +133,20 @@ def init(server, eventHandler):
logger.info(event._message)
continue
if isinstance(event, bb.event.ParseProgress):
- x = event.sofar
- y = event.total
+ current, total = event.sofar, event.total
if os.isatty(sys.stdout.fileno()):
- sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( next(parsespin), x, y, x*100//y ) )
- sys.stdout.flush()
+ if not pbar:
+ pbar = progressbar.ProgressBar(widgets=widgets,
+ maxval=total).start()
+ pbar.update(current)
else:
- if x == 1:
+ if current == 1:
sys.stdout.write("Parsing .bb files, please wait...")
sys.stdout.flush()
- if x == y:
+ if current == total:
sys.stdout.write("done.")
sys.stdout.flush()
- if x == y:
+ if current == total:
print(("\nParsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors."
% ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors)))
continue
diff --git a/bitbake/lib/progressbar.py b/bitbake/lib/progressbar.py
new file mode 100644
index 000000000..b668647a3
--- /dev/null
+++ b/bitbake/lib/progressbar.py
@@ -0,0 +1,384 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-1 -*-
+#
+# progressbar - Text progressbar library for python.
+# Copyright (c) 2005 Nilton Volpato
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""Text progressbar library for python.
+
+This library provides a text mode progressbar. This is typically used
+to display the progress of a long running operation, providing a
+visual clue that processing is underway.
+
+The ProgressBar class manages the progress, and the format of the line
+is given by a number of widgets. A widget is an object that may
+display diferently depending on the state of the progress. There are
+three types of widget:
+- a string, which always shows itself;
+- a ProgressBarWidget, which may return a diferent value every time
+it's update method is called; and
+- a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it
+expands to fill the remaining width of the line.
+
+The progressbar module is very easy to use, yet very powerful. And
+automatically supports features like auto-resizing when available.
+"""
+
+from __future__ import division
+
+__author__ = "Nilton Volpato"
+__author_email__ = "first-name dot last-name @ gmail.com"
+__date__ = "2006-05-07"
+__version__ = "2.3-dev"
+
+import sys, time, os
+from array import array
+try:
+ from fcntl import ioctl
+ import termios
+except ImportError:
+ pass
+import signal
+try:
+ basestring
+except NameError:
+ basestring = (str,)
+
+class ProgressBarWidget(object):
+ """This is an element of ProgressBar formatting.
+
+ The ProgressBar object will call it's update value when an update
+ is needed. It's size may change between call, but the results will
+ not be good if the size changes drastically and repeatedly.
+ """
+ def update(self, pbar):
+ """Returns the string representing the widget.
+
+ The parameter pbar is a reference to the calling ProgressBar,
+ where one can access attributes of the class for knowing how
+ the update must be made.
+
+ At least this function must be overriden."""
+ pass
+
+class ProgressBarWidgetHFill(object):
+ """This is a variable width element of ProgressBar formatting.
+
+ The ProgressBar object will call it's update value, informing the
+ width this object must the made. This is like TeX \\hfill, it will
+ expand to fill the line. You can use more than one in the same
+ line, and they will all have the same width, and together will
+ fill the line.
+ """
+ def update(self, pbar, width):
+ """Returns the string representing the widget.
+
+ The parameter pbar is a reference to the calling ProgressBar,
+ where one can access attributes of the class for knowing how
+ the update must be made. The parameter width is the total
+ horizontal width the widget must have.
+
+ At least this function must be overriden."""
+ pass
+
+
+class ETA(ProgressBarWidget):
+ "Widget for the Estimated Time of Arrival"
+ def format_time(self, seconds):
+ return time.strftime('%H:%M:%S', time.gmtime(seconds))
+ def update(self, pbar):
+ if pbar.currval == 0:
+ return 'ETA: --:--:--'
+ elif pbar.finished:
+ return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
+ else:
+ elapsed = pbar.seconds_elapsed
+ eta = elapsed * pbar.maxval / pbar.currval - elapsed
+ return 'ETA: %s' % self.format_time(eta)
+
+class FileTransferSpeed(ProgressBarWidget):
+ "Widget for showing the transfer speed (useful for file transfers)."
+ def __init__(self, unit='B'):
+ self.unit = unit
+ self.fmt = '%6.2f %s'
+ self.prefixes = ['', 'K', 'M', 'G', 'T', 'P']
+ def update(self, pbar):
+ if pbar.seconds_elapsed < 2e-6:#== 0:
+ bps = 0.0
+ else:
+ bps = pbar.currval / pbar.seconds_elapsed
+ spd = bps
+ for u in self.prefixes:
+ if spd < 1000:
+ break
+ spd /= 1000
+ return self.fmt % (spd, u + self.unit + '/s')
+
+class RotatingMarker(ProgressBarWidget):
+ "A rotating marker for filling the bar of progress."
+ def __init__(self, markers='|/-\\'):
+ self.markers = markers
+ self.curmark = -1
+ def update(self, pbar):
+ if pbar.finished:
+ return self.markers[0]
+ self.curmark = (self.curmark + 1) % len(self.markers)
+ return self.markers[self.curmark]
+
+class Percentage(ProgressBarWidget):
+ "Just the percentage done."
+ def update(self, pbar):
+ return '%3d%%' % pbar.percentage()
+
+class SimpleProgress(ProgressBarWidget):
+ "Returns what is already done and the total, e.g.: '5 of 47'"
+ def __init__(self, sep=' of '):
+ self.sep = sep
+ def update(self, pbar):
+ return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval)
+
+class Bar(ProgressBarWidgetHFill):
+ "The bar of progress. It will stretch to fill the line."
+ def __init__(self, marker='#', left='|', right='|'):
+ self.marker = marker
+ self.left = left
+ self.right = right
+ def _format_marker(self, pbar):
+ if isinstance(self.marker, basestring):
+ return self.marker
+ else:
+ return self.marker.update(pbar)
+ def update(self, pbar, width):
+ percent = pbar.percentage()
+ cwidth = width - len(self.left) - len(self.right)
+ marked_width = int(percent * cwidth // 100)
+ m = self._format_marker(pbar)
+ bar = (self.left + (m * marked_width).ljust(cwidth) + self.right)
+ return bar
+
+class ReverseBar(Bar):
+ "The reverse bar of progress, or bar of regress. :)"
+ def update(self, pbar, width):
+ percent = pbar.percentage()
+ cwidth = width - len(self.left) - len(self.right)
+ marked_width = int(percent * cwidth // 100)
+ m = self._format_marker(pbar)
+ bar = (self.left + (m*marked_width).rjust(cwidth) + self.right)
+ return bar
+
+default_widgets = [Percentage(), ' ', Bar()]
+class ProgressBar(object):
+ """This is the ProgressBar class, it updates and prints the bar.
+
+ A common way of using it is like:
+ >>> pbar = ProgressBar().start()
+ >>> for i in xrange(100):
+ ... # do something
+ ... pbar.update(i+1)
+ ...
+ >>> pbar.finish()
+
+ You can also use a progressbar as an iterator:
+ >>> progress = ProgressBar()
+ >>> for i in progress(some_iterable):
+ ... # do something
+ ...
+
+ But anything you want to do is possible (well, almost anything).
+ You can supply different widgets of any type in any order. And you
+ can even write your own widgets! There are many widgets already
+ shipped and you should experiment with them.
+
+ The term_width parameter must be an integer or None. In the latter case
+ it will try to guess it, if it fails it will default to 80 columns.
+
+ When implementing a widget update method you may access any
+ attribute or function of the ProgressBar object calling the
+ widget's update method. The most important attributes you would
+ like to access are:
+ - currval: current value of the progress, 0 <= currval <= maxval
+ - maxval: maximum (and final) value of the progress
+ - finished: True if the bar has finished (reached 100%), False o/w
+ - start_time: the time when start() method of ProgressBar was called
+ - seconds_elapsed: seconds elapsed since start_time
+ - percentage(): percentage of the progress [0..100]. This is a method.
+
+ The attributes above are unlikely to change between different versions,
+ the other ones may change or cease to exist without notice, so try to rely
+ only on the ones documented above if you are extending the progress bar.
+ """
+
+ __slots__ = ('currval', 'fd', 'finished', 'last_update_time', 'maxval',
+ 'next_update', 'num_intervals', 'seconds_elapsed',
+ 'signal_set', 'start_time', 'term_width', 'update_interval',
+ 'widgets', '_iterable')
+
+ _DEFAULT_MAXVAL = 100
+
+ def __init__(self, maxval=None, widgets=default_widgets, term_width=None,
+ fd=sys.stderr):
+ self.maxval = maxval
+ self.widgets = widgets
+ self.fd = fd
+ self.signal_set = False
+ if term_width is not None:
+ self.term_width = term_width
+ else:
+ try:
+ self._handle_resize(None, None)
+ signal.signal(signal.SIGWINCH, self._handle_resize)
+ self.signal_set = True
+ except (SystemExit, KeyboardInterrupt):
+ raise
+ except:
+ self.term_width = int(os.environ.get('COLUMNS', 80)) - 1
+
+ self.currval = 0
+ self.finished = False
+ self.start_time = None
+ self.last_update_time = None
+ self.seconds_elapsed = 0
+ self._iterable = None
+
+ def __call__(self, iterable):
+ try:
+ self.maxval = len(iterable)
+ except TypeError:
+ # If the iterable has no length, then rely on the value provided
+ # by the user, otherwise fail.
+ if not (isinstance(self.maxval, (int, long)) and self.maxval > 0):
+ raise RuntimeError('Could not determine maxval from iterable. '
+ 'You must explicitly provide a maxval.')
+ self._iterable = iter(iterable)
+ self.start()
+ return self
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ try:
+ next = self._iterable.next()
+ self.update(self.currval + 1)
+ return next
+ except StopIteration:
+ self.finish()
+ raise
+
+ def _handle_resize(self, signum, frame):
+ h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2]
+ self.term_width = w
+
+ def percentage(self):
+ "Returns the percentage of the progress."
+ return self.currval * 100.0 / self.maxval
+
+ def _format_widgets(self):
+ r = []
+ hfill_inds = []
+ num_hfill = 0
+ currwidth = 0
+ for i, w in enumerate(self.widgets):
+ if isinstance(w, ProgressBarWidgetHFill):
+ r.append(w)
+ hfill_inds.append(i)
+ num_hfill += 1
+ elif isinstance(w, basestring):
+ r.append(w)
+ currwidth += len(w)
+ else:
+ weval = w.update(self)
+ currwidth += len(weval)
+ r.append(weval)
+ for iw in hfill_inds:
+ widget_width = int((self.term_width - currwidth) // num_hfill)
+ r[iw] = r[iw].update(self, widget_width)
+ return r
+
+ def _format_line(self):
+ return ''.join(self._format_widgets()).ljust(self.term_width)
+
+ def _next_update(self):
+ return int((int(self.num_intervals *
+ (self.currval / self.maxval)) + 1) *
+ self.update_interval)
+
+ def _need_update(self):
+ """Returns true when the progressbar should print an updated line.
+
+ You can override this method if you want finer grained control over
+ updates.
+
+ The current implementation is optimized to be as fast as possible and
+ as economical as possible in the number of updates. However, depending
+ on your usage you may want to do more updates. For instance, if your
+ progressbar stays in the same percentage for a long time, and you want
+ to update other widgets, like ETA, then you could return True after
+ some time has passed with no updates.
+
+ Ideally you could call self._format_line() and see if it's different
+ from the previous _format_line() call, but calling _format_line() takes
+ around 20 times more time than calling this implementation of
+ _need_update().
+ """
+ return self.currval >= self.next_update
+
+ def update(self, value):
+ "Updates the progress bar to a new value."
+ assert 0 <= value <= self.maxval, '0 <= %d <= %d' % (value, self.maxval)
+ self.currval = value
+ if not self._need_update():
+ return
+ if self.start_time is None:
+ raise RuntimeError('You must call start() before calling update()')
+ now = time.time()
+ self.seconds_elapsed = now - self.start_time
+ self.next_update = self._next_update()
+ self.fd.write(self._format_line() + '\r')
+ self.last_update_time = now
+
+ def start(self):
+ """Starts measuring time, and prints the bar at 0%.
+
+ It returns self so you can use it like this:
+ >>> pbar = ProgressBar().start()
+ >>> for i in xrange(100):
+ ... # do something
+ ... pbar.update(i+1)
+ ...
+ >>> pbar.finish()
+ """
+ if self.maxval is None:
+ self.maxval = self._DEFAULT_MAXVAL
+ assert self.maxval > 0
+
+ self.num_intervals = max(100, self.term_width)
+ self.update_interval = self.maxval / self.num_intervals
+ self.next_update = 0
+
+ self.start_time = self.last_update_time = time.time()
+ self.update(0)
+ return self
+
+ def finish(self):
+ """Used to tell the progress is finished."""
+ self.finished = True
+ self.update(self.maxval)
+ self.fd.write('\n')
+ if self.signal_set:
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)