diff --git a/waflib/Runner.py b/waflib/Runner.py index 7535c83d..975c3292 100644 --- a/waflib/Runner.py +++ b/waflib/Runner.py @@ -226,6 +226,10 @@ class Parallel(object): pass else: if cond: + # The most common reason is conflicting build order declaration + # for example: "X run_after Y" and "Y run_after X" + # Another can be changing "run_after" dependencies while the build is running + # for example: updating "tsk.run_after" in the "runnable_status" method lst = [] for tsk in self.postponed: deps = [id(x) for x in tsk.run_after if not x.hasrun] @@ -298,6 +302,8 @@ class Parallel(object): def mark_finished(self, tsk): def try_unfreeze(x): # DAG ancestors are likely to be in the incomplete set + # This assumes that the run_after contents have not changed + # after the build starts, else a deadlock may occur if x in self.incomplete: # TODO remove dependencies to free some memory? # x.run_after.remove(tsk) diff --git a/waflib/Task.py b/waflib/Task.py index ffe7b16e..fd7a453f 100644 --- a/waflib/Task.py +++ b/waflib/Task.py @@ -590,6 +590,9 @@ class Task(evil): """ Run this task only after the given *task*. + Calling this method from :py:meth:`waflib.Task.Task.runnable_status` may cause + build deadlocks; see :py:meth:`waflib.Tools.fc.fc.runnable_status` for details. + :param task: task :type task: :py:class:`waflib.Task.Task` """ diff --git a/waflib/Tools/fc.py b/waflib/Tools/fc.py index 621eb502..d9e8d8c4 100644 --- a/waflib/Tools/fc.py +++ b/waflib/Tools/fc.py @@ -121,6 +121,8 @@ class fc(Task.Task): for k in ins.keys(): for a in ins[k]: a.run_after.update(outs[k]) + for x in outs[k]: + self.generator.bld.producer.revdeps[x].add(a) # the scanner cannot output nodes, so we have to set them # ourselves as task.dep_nodes (additional input nodes)