summaryrefslogtreecommitdiffstats
path: root/bitbake/lib
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2016-06-23 22:59:06 +1200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-07-08 09:57:26 +0100
commit0e3281f68b511ab7bf9d6b1c1f905ec794d3032a (patch)
tree3204dcb4d2eb439a3d8559d160aeb8711ef68f26 /bitbake/lib
parentac5e720575dd3c8f86514a7a646de82c8cc28e17 (diff)
downloadpoky-0e3281f68b511ab7bf9d6b1c1f905ec794d3032a.tar.gz
bitbake: lib/bb/progress: add MultiStageProgressReporter
Add a class to help report progress in a task that consists of multiple stages, some of which may have internal progress (do_rootfs within OpenEmbedded is one example). Each stage is weighted to try to give a reasonable representation of progress over time. Part of the implementation for [YOCTO #5383]. (Bitbake rev: 751b75602872a89e8b1a7c03269bc0fdaa149c6f) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib')
-rw-r--r--bitbake/lib/bb/progress.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/bitbake/lib/bb/progress.py b/bitbake/lib/bb/progress.py
index bab8e9465d..93e42dfcdb 100644
--- a/bitbake/lib/bb/progress.py
+++ b/bitbake/lib/bb/progress.py
@@ -20,6 +20,7 @@ BitBake progress handling code
20import sys 20import sys
21import re 21import re
22import time 22import time
23import inspect
23import bb.event 24import bb.event
24import bb.build 25import bb.build
25 26
@@ -84,3 +85,113 @@ class OutOfProgressHandler(ProgressHandler):
84 progress = (float(nums[-1][0]) / float(nums[-1][1])) * 100 85 progress = (float(nums[-1][0]) / float(nums[-1][1])) * 100
85 self.update(progress) 86 self.update(progress)
86 super(OutOfProgressHandler, self).write(string) 87 super(OutOfProgressHandler, self).write(string)
88
89class MultiStageProgressReporter(object):
90 """
91 Class which allows reporting progress without the caller
92 having to know where they are in the overall sequence. Useful
93 for tasks made up of python code spread across multiple
94 classes / functions - the progress reporter object can
95 be passed around or stored at the object level and calls
96 to next_stage() and update() made whereever needed.
97 """
98 def __init__(self, d, stage_weights, debug=False):
99 """
100 Initialise the progress reporter.
101
102 Parameters:
103 * d: the datastore (needed for firing the events)
104 * stage_weights: a list of weight values, one for each stage.
105 The value is scaled internally so you only need to specify
106 values relative to other values in the list, so if there
107 are two stages and the first takes 2s and the second takes
108 10s you would specify [2, 10] (or [1, 5], it doesn't matter).
109 * debug: specify True (and ensure you call finish() at the end)
110 in order to show a printout of the calculated stage weights
111 based on timing each stage. Use this to determine what the
112 weights should be when you're not sure.
113 """
114 self._data = d
115 total = sum(stage_weights)
116 self._stage_weights = [float(x)/total for x in stage_weights]
117 self._stage = -1
118 self._base_progress = 0
119 # Send an initial progress event so the bar gets shown
120 self._fire_progress(0)
121 self._debug = debug
122 self._finished = False
123 if self._debug:
124 self._last_time = time.time()
125 self._stage_times = []
126 self._stage_total = None
127 self._callers = []
128
129 def _fire_progress(self, taskprogress):
130 bb.event.fire(bb.build.TaskProgress(taskprogress), self._data)
131
132 def next_stage(self, stage_total=None):
133 """
134 Move to the next stage.
135 Parameters:
136 * stage_total: optional total for progress within the stage,
137 see update() for details
138 NOTE: you need to call this before the first stage.
139 """
140 self._stage += 1
141 self._stage_total = stage_total
142 if self._stage == 0:
143 # First stage
144 if self._debug:
145 self._last_time = time.time()
146 else:
147 if self._stage < len(self._stage_weights):
148 self._base_progress = sum(self._stage_weights[:self._stage]) * 100
149 if self._debug:
150 currtime = time.time()
151 self._stage_times.append(currtime - self._last_time)
152 self._last_time = currtime
153 self._callers.append(inspect.getouterframes(inspect.currentframe())[1])
154 elif not self._debug:
155 bb.warn('ProgressReporter: current stage beyond declared number of stages')
156 self._base_progress = 100
157 self._fire_progress(self._base_progress)
158
159 def update(self, stage_progress):
160 """
161 Update progress within the current stage.
162 Parameters:
163 * stage_progress: progress value within the stage. If stage_total
164 was specified when next_stage() was last called, then this
165 value is considered to be out of stage_total, otherwise it should
166 be a percentage value from 0 to 100.
167 """
168 if self._stage_total:
169 stage_progress = (float(stage_progress) / self._stage_total) * 100
170 if self._stage < 0:
171 bb.warn('ProgressReporter: update called before first call to next_stage()')
172 elif self._stage < len(self._stage_weights):
173 progress = self._base_progress + (stage_progress * self._stage_weights[self._stage])
174 else:
175 progress = self._base_progress
176 if progress > 100:
177 progress = 100
178 self._fire_progress(progress)
179
180 def finish(self):
181 if self._finished:
182 return
183 self._finished = True
184 if self._debug:
185 import math
186 self._stage_times.append(time.time() - self._last_time)
187 mintime = max(min(self._stage_times), 0.01)
188 self._callers.append(None)
189 stage_weights = [int(math.ceil(x / mintime)) for x in self._stage_times]
190 bb.warn('Stage weights: %s' % stage_weights)
191 out = []
192 for stage_weight, caller in zip(stage_weights, self._callers):
193 if caller:
194 out.append('Up to %s:%d: %d' % (caller[1], caller[2], stage_weight))
195 else:
196 out.append('Up to finish: %d' % stage_weight)
197 bb.warn('Stage times:\n %s' % '\n '.join(out))