runqueue.py: Improve the progress indicator by skipping tasks that have already run...
[bitbake.git] / lib / bb / runqueue.py
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 """
5 BitBake 'RunQueue' implementation
6
7 Handles preparation and execution of a queue of tasks
8 """
9
10 # Copyright (C) 2006  Richard Purdie
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License version 2 as
14 # published by the Free Software Foundation.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License along
22 # with this program; if not, write to the Free Software Foundation, Inc.,
23 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
25 from bb import msg, data, fetch, event, mkdirhier, utils
26 from sets import Set 
27 import bb, os, sys
28 import signal
29
30 class TaskFailure(Exception):
31     """Exception raised when a task in a runqueue fails"""
32     def __init__(self, x): 
33         self.args = x
34
35
36 class RunQueueStats:
37     """
38     Holds statistics on the tasks handled by the associated runQueue
39     """
40     def __init__(self):
41         self.completed = 0
42         self.skipped = 0
43         self.failed = 0
44
45     def taskFailed(self):
46         self.failed = self.failed + 1
47
48     def taskCompleted(self):
49         self.completed = self.completed + 1
50
51     def taskSkipped(self):
52         self.skipped = self.skipped + 1
53
54 class RunQueue:
55     """
56     BitBake Run Queue implementation
57     """
58     def __init__(self, cooker, cfgData, dataCache, taskData, targets):
59         self.reset_runqueue()
60         self.cooker = cooker
61         self.dataCache = dataCache
62         self.taskData = taskData
63         self.targets = targets
64
65         self.number_tasks = int(bb.data.getVar("BB_NUMBER_THREADS", cfgData) or 1)
66
67     def reset_runqueue(self):
68
69         self.runq_fnid = []
70         self.runq_task = []
71         self.runq_depends = []
72         self.runq_revdeps = []
73         self.runq_weight = []
74         self.prio_map = []
75
76     def get_user_idstring(self, task):
77         fn = self.taskData.fn_index[self.runq_fnid[task]]
78         taskname = self.runq_task[task]
79         return "%s, %s" % (fn, taskname)
80
81     def prepare_runqueue(self):
82         """
83         Turn a set of taskData into a RunQueue and compute data needed 
84         to optimise the execution order.
85         """
86
87         depends = []
88         runq_weight1 = []
89         runq_build = []
90         runq_done = []
91
92         taskData = self.taskData
93
94         if len(taskData.tasks_name) == 0:
95             # Nothing to do
96             return
97
98         bb.msg.note(1, bb.msg.domain.RunQueue, "Preparing runqueue")
99
100         for task in range(len(taskData.tasks_name)):
101             fnid = taskData.tasks_fnid[task]
102             fn = taskData.fn_index[fnid]
103             task_deps = self.dataCache.task_deps[fn]
104
105             if fnid not in taskData.failed_fnids:
106
107                 depends = taskData.tasks_tdepends[task]
108
109                 # Resolve Depends
110                 if 'deptask' in task_deps and taskData.tasks_name[task] in task_deps['deptask']:
111                     taskname = task_deps['deptask'][taskData.tasks_name[task]]
112                     for depid in taskData.depids[fnid]:
113                         # Won't be in build_targets if ASSUME_PROVIDED
114                         if depid in taskData.build_targets:
115                             depdata = taskData.build_targets[depid][0]
116                             if depdata:
117                                 dep = taskData.fn_index[depdata]
118                                 depends.append(taskData.gettask_id(dep, taskname))
119
120                 # Resolve Runtime Depends
121                 if 'rdeptask' in task_deps and taskData.tasks_name[task] in task_deps['rdeptask']:
122                     taskname = task_deps['rdeptask'][taskData.tasks_name[task]]
123                     for depid in taskData.rdepids[fnid]:
124                         if depid in taskData.run_targets:
125                             depdata = taskData.run_targets[depid][0]
126                             if depdata:
127                                 dep = taskData.fn_index[depdata]
128                                 depends.append(taskData.gettask_id(dep, taskname))
129
130                 idepends = taskData.tasks_idepends[task]
131                 for idepend in idepends:
132                     depid = int(idepend.split(":")[0])
133                     if depid in taskData.build_targets:
134                         # Won't be in build_targets if ASSUME_PROVIDED
135                         depdata = taskData.build_targets[depid][0]
136                         if depdata:
137                             dep = taskData.fn_index[depdata]
138                             depends.append(taskData.gettask_id(dep, idepend.split(":")[1]))
139
140                 def add_recursive_build(depid):
141                     """
142                     Add build depends of depid to depends
143                     (if we've not see it before)
144                     (calls itself recursively)
145                     """
146                     if str(depid) in dep_seen:
147                         return
148                     dep_seen.append(depid)
149                     if depid in taskData.build_targets:
150                         depdata = taskData.build_targets[depid][0]
151                         if depdata:
152                             dep = taskData.fn_index[depdata]
153                             # Need to avoid creating new tasks here
154                             taskid = taskData.gettask_id(dep, taskname, False)
155                             if taskid:
156                                 depends.append(taskid)
157                                 fnid = taskData.tasks_fnid[taskid]
158                             else:
159                                 fnid = taskData.getfn_id(dep)
160                             for nextdepid in taskData.depids[fnid]:
161                                 if nextdepid not in dep_seen:
162                                     add_recursive_build(nextdepid)
163                             for nextdepid in taskData.rdepids[fnid]:
164                                 if nextdepid not in rdep_seen:
165                                     add_recursive_run(nextdepid)
166                             idepends = taskData.tasks_idepends[depid]
167                             for idepend in idepends:
168                                 nextdepid = int(idepend.split(":")[0])
169                                 if nextdepid not in dep_seen:
170                                     add_recursive_build(nextdepid)
171
172                 def add_recursive_run(rdepid):
173                     """
174                     Add runtime depends of rdepid to depends
175                     (if we've not see it before)
176                     (calls itself recursively)
177                     """
178                     if str(rdepid) in rdep_seen:
179                         return
180                     rdep_seen.append(rdepid)
181                     if rdepid in taskData.run_targets:
182                         depdata = taskData.run_targets[rdepid][0]
183                         if depdata:
184                             dep = taskData.fn_index[depdata]
185                             # Need to avoid creating new tasks here
186                             taskid = taskData.gettask_id(dep, taskname, False)
187                             if taskid:
188                                 depends.append(taskid)
189                                 fnid = taskData.tasks_fnid[taskid]
190                             else:
191                                 fnid = taskData.getfn_id(dep)
192                             for nextdepid in taskData.depids[fnid]:
193                                 if nextdepid not in dep_seen:
194                                     add_recursive_build(nextdepid)
195                             for nextdepid in taskData.rdepids[fnid]:
196                                 if nextdepid not in rdep_seen:
197                                     add_recursive_run(nextdepid)
198                             idepends = taskData.tasks_idepends[rdepid]
199                             for idepend in idepends:
200                                 nextdepid = int(idepend.split(":")[0])
201                                 if nextdepid not in dep_seen:
202                                     add_recursive_build(nextdepid)
203
204
205                 # Resolve Recursive Runtime Depends
206                 # Also includes all thier build depends, intertask depends and runtime depends
207                 if 'recrdeptask' in task_deps and taskData.tasks_name[task] in task_deps['recrdeptask']:
208                     for taskname in task_deps['recrdeptask'][taskData.tasks_name[task]].split():
209                         dep_seen = []
210                         rdep_seen = []
211                         idep_seen = []
212                         for depid in taskData.depids[fnid]:
213                             add_recursive_build(depid)
214                         for rdepid in taskData.rdepids[fnid]:
215                             add_recursive_run(rdepid)
216                         for idepend in idepends:
217                             depid = int(idepend.split(":")[0])
218                             add_recursive_build(depid)
219
220                 #Prune self references
221                 if task in depends:
222                     newdep = []
223                     bb.msg.debug(2, bb.msg.domain.RunQueue, "Task %s (%s %s) contains self reference! %s" % (task, taskData.fn_index[taskData.tasks_fnid[task]], taskData.tasks_name[task], depends))
224                     for dep in depends:
225                        if task != dep:
226                           newdep.append(dep)
227                     depends = newdep
228
229
230             self.runq_fnid.append(taskData.tasks_fnid[task])
231             self.runq_task.append(taskData.tasks_name[task])
232             self.runq_depends.append(Set(depends))
233             self.runq_revdeps.append(Set())
234             self.runq_weight.append(0)
235
236             runq_weight1.append(0)
237             runq_build.append(0)
238             runq_done.append(0)
239
240         bb.msg.note(2, bb.msg.domain.RunQueue, "Marking Active Tasks")
241
242         def mark_active(listid, depth):
243             """
244             Mark an item as active along with its depends
245             (calls itself recursively)
246             """
247
248             if runq_build[listid] == 1:
249                 return
250
251             runq_build[listid] = 1
252
253             depends = self.runq_depends[listid]
254             for depend in depends:
255                 mark_active(depend, depth+1)
256
257         for target in self.targets:
258             targetid = taskData.getbuild_id(target[0])
259
260             if targetid not in taskData.build_targets:
261                 continue
262
263             if targetid in taskData.failed_deps:
264                 continue
265
266             fnid = taskData.build_targets[targetid][0]
267
268             # Remove stamps for targets if force mode active
269             if self.cooker.configuration.force:
270                 fn = taskData.fn_index[fnid]
271                 bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (target[1], fn))
272                 bb.build.del_stamp(target[1], self.dataCache, fn)
273
274             if fnid in taskData.failed_fnids:
275                 continue
276
277             listid = taskData.tasks_lookup[fnid][target[1]]
278
279             mark_active(listid, 1)
280
281         # Prune inactive tasks
282         maps = []
283         delcount = 0
284         for listid in range(len(self.runq_fnid)):
285             if runq_build[listid-delcount] == 1:
286                 maps.append(listid-delcount)
287             else:
288                 del self.runq_fnid[listid-delcount]
289                 del self.runq_task[listid-delcount]
290                 del self.runq_depends[listid-delcount]
291                 del self.runq_weight[listid-delcount]
292                 del runq_weight1[listid-delcount]
293                 del runq_build[listid-delcount]
294                 del runq_done[listid-delcount]
295                 del self.runq_revdeps[listid-delcount]
296                 delcount = delcount + 1
297                 maps.append(-1)
298
299         if len(self.runq_fnid) == 0:
300             if not taskData.abort:
301                 bb.msg.note(1, bb.msg.domain.RunQueue, "All possible tasks have been run but build incomplete (--continue mode). See errors above for incomplete tasks.")
302                 return
303             bb.msg.fatal(bb.msg.domain.RunQueue, "No active tasks and not in --continue mode?! Please report this bug.")
304
305         bb.msg.note(2, bb.msg.domain.RunQueue, "Pruned %s inactive tasks, %s left" % (delcount, len(self.runq_fnid)))
306
307         for listid in range(len(self.runq_fnid)):
308             newdeps = []
309             origdeps = self.runq_depends[listid]
310             for origdep in origdeps:
311                 if maps[origdep] == -1:
312                     bb.msg.fatal(bb.msg.domain.RunQueue, "Invalid mapping - Should never happen!")
313                 newdeps.append(maps[origdep])
314             self.runq_depends[listid] = Set(newdeps)
315
316         bb.msg.note(2, bb.msg.domain.RunQueue, "Assign Weightings")
317
318         for listid in range(len(self.runq_fnid)):
319             for dep in self.runq_depends[listid]:
320                 self.runq_revdeps[dep].add(listid)
321
322         endpoints = []
323         for listid in range(len(self.runq_fnid)):
324             revdeps = self.runq_revdeps[listid]
325             if len(revdeps) == 0:
326                 runq_done[listid] = 1
327                 self.runq_weight[listid] = 1
328                 endpoints.append(listid)
329             for dep in revdeps:
330                 if dep in self.runq_depends[listid]:
331                     #self.dump_data(taskData)
332                     bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) has circular dependency on %s (%s)" % (taskData.fn_index[self.runq_fnid[dep]], self.runq_task[dep] , taskData.fn_index[self.runq_fnid[listid]], self.runq_task[listid]))
333             runq_weight1[listid] = len(revdeps)
334
335         bb.msg.note(2, bb.msg.domain.RunQueue, "Compute totals (have %s endpoint(s))" % len(endpoints))
336
337         while 1:
338             next_points = []
339             for listid in endpoints:
340                 for revdep in self.runq_depends[listid]:
341                     self.runq_weight[revdep] = self.runq_weight[revdep] + self.runq_weight[listid]
342                     runq_weight1[revdep] = runq_weight1[revdep] - 1
343                     if runq_weight1[revdep] == 0:
344                         next_points.append(revdep)
345                         runq_done[revdep] = 1
346             endpoints = next_points
347             if len(next_points) == 0:
348                 break           
349
350         # Sanity Checks
351         for task in range(len(self.runq_fnid)):
352             if runq_done[task] == 0:
353                 seen = []
354                 deps_seen = []
355                 def print_chain(taskid, finish):
356                     seen.append(taskid)
357                     for revdep in self.runq_revdeps[taskid]:
358                         if runq_done[revdep] == 0 and revdep not in seen and not finish:
359                             bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) (depends: %s)" % (revdep, self.get_user_idstring(revdep), self.runq_depends[revdep]))
360                             if revdep in deps_seen:
361                                 bb.msg.error(bb.msg.domain.RunQueue, "Chain ends at Task %s (%s)" % (revdep, self.get_user_idstring(revdep)))
362                                 finish = True
363                                 return
364                             for dep in self.runq_depends[revdep]:
365                                 deps_seen.append(dep)
366                             print_chain(revdep, finish)
367                 print_chain(task, False)
368                 bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) not processed!\nThis is probably a circular dependency (the chain might be printed above)." % (task, self.get_user_idstring(task)))
369             if runq_weight1[task] != 0:
370                 bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) count not zero!" % (task, self.get_user_idstring(task)))
371
372         # Make a weight sorted map
373         from copy import deepcopy
374
375         sortweight = deepcopy(self.runq_weight)
376         sortweight.sort()
377         copyweight = deepcopy(self.runq_weight)
378         self.prio_map = []
379
380         for weight in sortweight:
381             idx = copyweight.index(weight)
382             self.prio_map.append(idx)
383             copyweight[idx] = -1
384         self.prio_map.reverse()
385
386         #self.dump_data(taskData)
387
388     def execute_runqueue(self):
389         """
390         Run the tasks in a queue prepared by prepare_runqueue
391         Upon failure, optionally try to recover the build using any alternate providers
392         (if the abort on failure configuration option isn't set)
393         """
394
395         failures = 0
396         while 1:
397             failed_fnids = []
398             try:
399                 self.execute_runqueue_internal()
400             finally:
401                 if self.master_process:
402                     failed_fnids = self.finish_runqueue()
403             if len(failed_fnids) == 0:
404                 return failures
405             if self.taskData.abort:
406                 raise bb.runqueue.TaskFailure(failed_fnids)
407             for fnid in failed_fnids:
408                 #print "Failure: %s %s %s" % (fnid, self.taskData.fn_index[fnid],  self.runq_task[fnid])
409                 self.taskData.fail_fnid(fnid)
410                 failures = failures + 1
411             self.reset_runqueue()
412             self.prepare_runqueue()
413
414     def execute_runqueue_initVars(self):
415
416         self.stats = RunQueueStats()
417
418         self.active_builds = 0
419         self.runq_buildable = []
420         self.runq_running = []
421         self.runq_complete = []
422         self.build_pids = {}
423         self.failed_fnids = []
424         self.master_process = True
425
426         # Mark initial buildable tasks
427         for task in range(len(self.runq_fnid)):
428             self.runq_running.append(0)
429             self.runq_complete.append(0)
430             if len(self.runq_depends[task]) == 0:
431                 self.runq_buildable.append(1)
432             else:
433                 self.runq_buildable.append(0)
434
435     def task_complete(self, task):
436         """
437         Mark a task as completed
438         Look at the reverse dependencies and mark any task with 
439         completed dependencies as buildable
440         """
441         self.runq_complete[task] = 1
442         for revdep in self.runq_revdeps[task]:
443             if self.runq_running[revdep] == 1:
444                 continue
445             if self.runq_buildable[revdep] == 1:
446                 continue
447             alldeps = 1
448             for dep in self.runq_depends[revdep]:
449                 if self.runq_complete[dep] != 1:
450                     alldeps = 0
451             if alldeps == 1:
452                 self.runq_buildable[revdep] = 1
453                 fn = self.taskData.fn_index[self.runq_fnid[revdep]]
454                 taskname = self.runq_task[revdep]
455                 bb.msg.debug(1, bb.msg.domain.RunQueue, "Marking task %s (%s, %s) as buildable" % (revdep, fn, taskname))
456
457     def get_next_task(self):
458         """
459         Return the id of the highest priority task that is buildable
460         """
461         for task1 in range(len(self.runq_fnid)):
462             task = self.prio_map[task1]
463             if self.runq_running[task] == 1:
464                 continue
465             if self.runq_buildable[task] == 1:
466                 return task
467         return None
468
469     def execute_runqueue_internal(self):
470         """
471         Run the tasks in a queue prepared by prepare_runqueue
472         """
473
474         bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue")
475
476         self.execute_runqueue_initVars()
477
478         if len(self.runq_fnid) == 0:
479             # nothing to do
480             return []
481
482         def sigint_handler(signum, frame):
483             raise KeyboardInterrupt
484
485         # Find any tasks with current stamps and remove them from the queue
486         for task1 in range(len(self.runq_fnid)):
487             task = self.prio_map[task1]
488             fn = self.taskData.fn_index[self.runq_fnid[task]]
489             taskname = self.runq_task[task]
490             if bb.build.stamp_is_current(taskname, self.dataCache, fn):
491                 bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task)))
492                 self.runq_running[task] = 1
493                 self.task_complete(task)
494                 self.stats.taskCompleted()
495                 self.stats.taskSkipped()
496
497         while True:
498             task = self.get_next_task()
499             if task is not None:
500                 fn = self.taskData.fn_index[self.runq_fnid[task]]
501
502                 taskname = self.runq_task[task]
503                 if bb.build.stamp_is_current(taskname, self.dataCache, fn):
504                     bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task)))
505                     self.runq_running[task] = 1
506                     self.task_complete(task)
507                     self.stats.taskCompleted()
508                     self.stats.taskSkipped()
509                     continue
510
511                 bb.msg.note(1, bb.msg.domain.RunQueue, "Running task %d of %d (ID: %s, %s)" % (self.stats.completed + self.active_builds + 1, len(self.runq_fnid), task, self.get_user_idstring(task)))
512                 try: 
513                     pid = os.fork() 
514                 except OSError, e: 
515                     bb.msg.fatal(bb.msg.domain.RunQueue, "fork failed: %d (%s)" % (e.errno, e.strerror))
516                 if pid == 0:
517                     # Bypass master process' handling
518                     self.master_process = False
519                     # Stop Ctrl+C being sent to children
520                     # signal.signal(signal.SIGINT, signal.SIG_IGN)
521                     # Make the child the process group leader
522                     os.setpgid(0, 0)
523                     newsi = os.open('/dev/null', os.O_RDWR)
524                     os.dup2(newsi, sys.stdin.fileno())
525                     self.cooker.configuration.cmd = taskname[3:]
526                     try: 
527                         self.cooker.tryBuild(fn, False)
528                     except bb.build.EventException:
529                         bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed")
530                         sys.exit(1)
531                     except:
532                         bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed")
533                         raise
534                     sys.exit(0)
535                 self.build_pids[pid] = task
536                 self.runq_running[task] = 1
537                 self.active_builds = self.active_builds + 1
538                 if self.active_builds < self.number_tasks:
539                     continue
540             if self.active_builds > 0:
541                 result = os.waitpid(-1, 0)
542                 self.active_builds = self.active_builds - 1
543                 task = self.build_pids[result[0]]
544                 if result[1] != 0:
545                     del self.build_pids[result[0]]
546                     bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task)))
547                     self.failed_fnids.append(self.runq_fnid[task])
548                     self.stats.taskFailed()
549                     break
550                 self.task_complete(task)
551                 self.stats.taskCompleted()
552                 del self.build_pids[result[0]]
553                 continue
554             return
555
556     def finish_runqueue(self):
557         try:
558             while self.active_builds > 0:
559                 bb.msg.note(1, bb.msg.domain.RunQueue, "Waiting for %s active tasks to finish" % self.active_builds)
560                 tasknum = 1
561                 for k, v in self.build_pids.iteritems():
562                      bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v), k))
563                      tasknum = tasknum + 1
564                 result = os.waitpid(-1, 0)
565                 task = self.build_pids[result[0]]
566                 if result[1] != 0:
567                      bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task)))
568                      self.failed_fnids.append(self.runq_fnid[task])
569                      self.stats.taskFailed()
570                 del self.build_pids[result[0]]
571                 self.active_builds = self.active_builds - 1
572             bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed))
573             return self.failed_fnids
574         except KeyboardInterrupt:
575             bb.msg.note(1, bb.msg.domain.RunQueue, "Sending SIGINT to remaining %s tasks" % self.active_builds)
576             for k, v in self.build_pids.iteritems():
577                  try:
578                      os.kill(-k, signal.SIGINT)
579                  except:
580                      pass
581             raise
582
583         # Sanity Checks
584         for task in range(len(self.runq_fnid)):
585             if self.runq_buildable[task] == 0:
586                 bb.msg.error(bb.msg.domain.RunQueue, "Task %s never buildable!" % task)
587             if self.runq_running[task] == 0:
588                 bb.msg.error(bb.msg.domain.RunQueue, "Task %s never ran!" % task)
589             if self.runq_complete[task] == 0:
590                 bb.msg.error(bb.msg.domain.RunQueue, "Task %s never completed!" % task)
591
592         bb.msg.note(1, bb.msg.domain.RunQueue, "Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed." % (self.stats.completed, self.stats.skipped, self.stats.failed))
593
594         return self.failed_fnids
595
596     def dump_data(self, taskQueue):
597         """
598         Dump some debug information on the internal data structures
599         """
600         bb.msg.debug(3, bb.msg.domain.RunQueue, "run_tasks:")
601         for task in range(len(self.runq_fnid)):
602                 bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s   Deps %s RevDeps %s" % (task, 
603                         taskQueue.fn_index[self.runq_fnid[task]], 
604                         self.runq_task[task], 
605                         self.runq_weight[task], 
606                         self.runq_depends[task], 
607                         self.runq_revdeps[task]))
608
609         bb.msg.debug(3, bb.msg.domain.RunQueue, "sorted_tasks:")
610         for task1 in range(len(self.runq_fnid)):
611             if task1 in self.prio_map:
612                 task = self.prio_map[task1]
613                 bb.msg.debug(3, bb.msg.domain.RunQueue, " (%s)%s - %s: %s   Deps %s RevDeps %s" % (task, 
614                         taskQueue.fn_index[self.runq_fnid[task]], 
615                         self.runq_task[task], 
616                         self.runq_weight[task], 
617                         self.runq_depends[task], 
618                         self.runq_revdeps[task]))