"""
from bb import msg, data, fetch, event, mkdirhier, utils
+from sets import Set
import bb, os, sys
class TaskFailure(Exception):
"""Exception raised when a task in a runqueue fails"""
-
- def __init__(self, fnid, taskname):
- self.args = fnid, taskname
+ def __init__(self, x):
+ self.args = x
class RunQueue:
"""
dep = taskData.fn_index[depdata]
depends.append(taskData.gettask_id(dep, taskname))
+ def add_recursive_build(depid):
+ """
+ Add build depends of depid to depends
+ (if we've not see it before)
+ (calls itself recursively)
+ """
+ if str(depid) in dep_seen:
+ return
+ dep_seen.append(depid)
+ if depid in taskData.build_targets:
+ depdata = taskData.build_targets[depid][0]
+ if depdata:
+ dep = taskData.fn_index[depdata]
+ taskid = taskData.gettask_id(dep, taskname)
+ depends.append(taskid)
+ fnid = taskData.tasks_fnid[taskid]
+ for nextdepid in taskData.depids[fnid]:
+ if nextdepid not in dep_seen:
+ add_recursive_build(nextdepid)
+ for nextdepid in taskData.rdepids[fnid]:
+ if nextdepid not in rdep_seen:
+ add_recursive_run(nextdepid)
+
def add_recursive_run(rdepid):
"""
- Add runtime depends of rdepid to depends, if
- we've not see it before
+ Add runtime depends of rdepid to depends
+ (if we've not see it before)
(calls itself recursively)
"""
if str(rdepid) in rdep_seen:
taskid = taskData.gettask_id(dep, taskname)
depends.append(taskid)
fnid = taskData.tasks_fnid[taskid]
+ for nextdepid in taskData.depids[fnid]:
+ if nextdepid not in dep_seen:
+ add_recursive_build(nextdepid)
for nextdepid in taskData.rdepids[fnid]:
if nextdepid not in rdep_seen:
add_recursive_run(nextdepid)
+
# Resolve Recursive Runtime Depends
+ # Also includes all Build Depends (and their runtime depends)
if 'recrdeptask' in task_deps and taskData.tasks_name[task] in task_deps['recrdeptask']:
+ dep_seen = []
rdep_seen = []
taskname = task_deps['recrdeptask'][taskData.tasks_name[task]]
+ for depid in taskData.depids[fnid]:
+ add_recursive_build(depid)
for rdepid in taskData.rdepids[fnid]:
add_recursive_run(rdepid)
self.runq_fnid.append(taskData.tasks_fnid[task])
self.runq_task.append(taskData.tasks_name[task])
- self.runq_depends.append(depends)
- self.runq_revdeps.append([])
+ self.runq_depends.append(Set(depends))
+ self.runq_revdeps.append(Set())
self.runq_weight.append(0)
runq_weight1.append(0)
if targetid in taskData.failed_deps:
continue
+ if targetid not in taskData.build_targets:
+ continue
+
fnid = taskData.build_targets[targetid][0]
if fnid in taskData.failed_fnids:
continue
- fnids = taskData.matches_in_list(self.runq_fnid, fnid)
- tasks = taskData.matches_in_list(self.runq_task, target[1])
-
- listid = taskData.both_contain(fnids, tasks)
+ listid = taskData.tasks_lookup[fnid][target[1]]
mark_active(listid, 1)
delcount = delcount + 1
maps.append(-1)
+ if len(self.runq_fnid) == 0:
+ if not taskData.abort:
+ 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.")
+ return
+ bb.msg.fatal(bb.msg.domain.RunQueue, "No active tasks and not in --continue mode?! Please report this bug.")
+
bb.msg.note(2, bb.msg.domain.RunQueue, "Pruned %s inactive tasks, %s left" % (delcount, len(self.runq_fnid)))
for listid in range(len(self.runq_fnid)):
if maps[origdep] == -1:
bb.msg.fatal(bb.msg.domain.RunQueue, "Invalid mapping - Should never happen!")
newdeps.append(maps[origdep])
- self.runq_depends[listid] = newdeps
+ self.runq_depends[listid] = Set(newdeps)
bb.msg.note(2, bb.msg.domain.RunQueue, "Assign Weightings")
for listid in range(len(self.runq_fnid)):
for dep in self.runq_depends[listid]:
- if dep not in self.runq_revdeps[dep]:
- self.runq_revdeps[dep].append(listid)
+ self.runq_revdeps[dep].add(listid)
endpoints = []
for listid in range(len(self.runq_fnid)):
# Sanity Checks
for task in range(len(self.runq_fnid)):
if runq_done[task] == 0:
- bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) not processed!" % (task, self.get_user_idstring(task, taskData)))
+ seen = []
+ deps_seen = []
+ def print_chain(taskid, finish):
+ seen.append(taskid)
+ for revdep in self.runq_revdeps[taskid]:
+ if runq_done[revdep] == 0 and revdep not in seen and not finish:
+ bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) (depends: %s)" % (revdep, self.get_user_idstring(revdep, taskData), self.runq_depends[revdep]))
+ if revdep in deps_seen:
+ bb.msg.error(bb.msg.domain.RunQueue, "Chain ends at Task %s (%s)" % (revdep, self.get_user_idstring(revdep, taskData)))
+ finish = True
+ return
+ for dep in self.runq_depends[revdep]:
+ deps_seen.append(dep)
+ print_chain(revdep, finish)
+ print_chain(task, False)
+ 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, taskData)))
if runq_weight1[task] != 0:
bb.msg.fatal(bb.msg.domain.RunQueue, "Task %s (%s) count not zero!" % (task, self.get_user_idstring(task, taskData)))
copyweight[idx] = -1
self.prio_map.reverse()
+ #self.dump_data(taskData)
+
def execute_runqueue(self, cooker, cfgData, dataCache, taskData, runlist):
"""
Run the tasks in a queue prepared by prepare_runqueue
failures = 0
while 1:
- try:
- self.execute_runqueue_internal(cooker, cfgData, dataCache, taskData)
+ failed_fnids = self.execute_runqueue_internal(cooker, cfgData, dataCache, taskData)
+ if len(failed_fnids) == 0:
return failures
- except bb.runqueue.TaskFailure, (fnid, taskname):
- if cooker.configuration.abort:
- raise
+ if taskData.abort:
+ raise bb.runqueue.TaskFailure(failed_fnids)
+ for fnid in failed_fnids:
+ #print "Failure: %s %s %s" % (fnid, taskData.fn_index[fnid], self.runq_task[fnid])
taskData.fail_fnid(fnid)
- self.reset_runqueue()
- self.prepare_runqueue(cfgData, dataCache, taskData, runlist)
failures = failures + 1
+ self.reset_runqueue()
+ self.prepare_runqueue(cfgData, dataCache, taskData, runlist)
def execute_runqueue_internal(self, cooker, cfgData, dataCache, taskData):
"""
Run the tasks in a queue prepared by prepare_runqueue
"""
+ import signal
bb.msg.note(1, bb.msg.domain.RunQueue, "Executing runqueue")
runq_complete = []
active_builds = 0
build_pids = {}
+ failed_fnids = []
+
+ if len(self.runq_fnid) == 0:
+ # nothing to do
+ return
+
+ def sigint_handler(signum, frame):
+ raise KeyboardInterrupt
def get_next_task(data):
"""
taskname = self.runq_task[task]
if bb.build.stamp_is_current_cache(dataCache, fn, taskname):
- bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task, taskData)))
- runq_running[task] = 1
- task_complete(self, task)
- continue
+ targetid = taskData.gettask_id(fn, taskname)
+ if not (targetid in taskData.external_targets and cooker.configuration.force):
+ bb.msg.debug(2, bb.msg.domain.RunQueue, "Stamp current task %s (%s)" % (task, self.get_user_idstring(task, taskData)))
+ runq_running[task] = 1
+ task_complete(self, task)
+ continue
bb.msg.debug(1, bb.msg.domain.RunQueue, "Running task %s (%s)" % (task, self.get_user_idstring(task, taskData)))
try:
except OSError, e:
bb.msg.fatal(bb.msg.domain.RunQueue, "fork failed: %d (%s)" % (e.errno, e.strerror))
if pid == 0:
+ # Bypass finally below
+ active_builds = 0
+ # Stop Ctrl+C being sent to children
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ sys.stdin = open('/dev/null', 'r')
cooker.configuration.cmd = taskname[3:]
try:
cooker.tryBuild(fn, False)
- except bb.build.EventException, e:
+ except bb.build.EventException:
bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed")
sys.exit(1)
except:
- sys.exit(1)
+ bb.msg.error(bb.msg.domain.Build, "Build of " + fn + " " + taskname + " failed")
+ raise
sys.exit(0)
build_pids[pid] = task
runq_running[task] = 1
if active_builds > 0:
result = os.waitpid(-1, 0)
active_builds = active_builds - 1
+ task = build_pids[result[0]]
if result[1] != 0:
- bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (build_pids[result[0]], self.get_user_idstring(build_pids[result[0]], taskData)))
- raise bb.runqueue.TaskFailure(self.runq_fnid[build_pids[result[0]]], self.runq_task[build_pids[result[0]]])
- task_complete(self, build_pids[result[0]])
+ del build_pids[result[0]]
+ bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task, taskData)))
+ failed_fnids.append(self.runq_fnid[task])
+ break
+ task_complete(self, task)
del build_pids[result[0]]
continue
break
- except SystemExit:
- raise
- except:
- bb.msg.error(bb.msg.domain.RunQueue, "Exception received")
- if active_builds > 0:
+ finally:
+ try:
+ orig_builds = active_builds
while active_builds > 0:
bb.msg.note(1, bb.msg.domain.RunQueue, "Waiting for %s active tasks to finish" % active_builds)
tasknum = 1
for k, v in build_pids.iteritems():
- bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v, taskData), k))
- tasknum = tasknum + 1
+ bb.msg.note(1, bb.msg.domain.RunQueue, "%s: %s (%s)" % (tasknum, self.get_user_idstring(v, taskData), k))
+ tasknum = tasknum + 1
result = os.waitpid(-1, 0)
- del build_pids[result[0]]
+ task = build_pids[result[0]]
+ if result[1] != 0:
+ bb.msg.error(bb.msg.domain.RunQueue, "Task %s (%s) failed" % (task, self.get_user_idstring(task, taskData)))
+ failed_fnids.append(self.runq_fnid[task])
+ del build_pids[result[0]]
active_builds = active_builds - 1
- raise
+ if orig_builds > 0:
+ return failed_fnids
+ except:
+ bb.msg.note(1, bb.msg.domain.RunQueue, "Sending SIGTERM to remaining %s tasks" % active_builds)
+ for k, v in build_pids.iteritems():
+ os.kill(k, signal.SIGTERM)
+ raise
# Sanity Checks
for task in range(len(self.runq_fnid)):
if runq_complete[task] == 0:
bb.msg.error(bb.msg.domain.RunQueue, "Task %s never completed!" % task)
- return 0
+ return failed_fnids
def dump_data(self, taskQueue):
"""