Author: William ML Leslie <[email protected]>
Branch: taskengine-sorted-optionals
Changeset: r84337:cb82010dadbc
Date: 2016-05-10 02:02 +1000
http://bitbucket.org/pypy/pypy/changeset/cb82010dadbc/

Log:    Optional dependencies should take part in ordering even if they
        become non-optional 'later'

diff --git a/rpython/translator/tool/taskengine.py 
b/rpython/translator/tool/taskengine.py
--- a/rpython/translator/tool/taskengine.py
+++ b/rpython/translator/tool/taskengine.py
@@ -13,7 +13,7 @@
 
                 tasks[task_name] = task, task_deps
 
-    def _plan(self, goals, skip=[]):
+    def _plan(self, goals, skip=()):
         skip = [toskip for toskip in skip if toskip not in goals]
 
         key = (tuple(goals), tuple(skip))
@@ -21,64 +21,46 @@
             return self._plan_cache[key]
         except KeyError:
             pass
-        constraints = []
-
-        def subgoals(task_name):
-            taskcallable, deps = self.tasks[task_name]
-            for dep in deps:
-                if dep.startswith('??'): # optional
-                    dep = dep[2:]
-                    if dep not in goals:
-                        continue
-                if dep.startswith('?'): # suggested
-                    dep = dep[1:]
-                    if dep in skip:
-                        continue
-                yield dep
-
-        seen = {}
-
-        def consider(subgoal):
-            if subgoal in seen:
-                return
-            else:
-                seen[subgoal] = True
-            constraints.append([subgoal])
-            deps = subgoals(subgoal)
-            for dep in deps:
-                constraints.append([subgoal, dep])
-                consider(dep)
-
-        for goal in goals:
-            consider(goal)
-
-        #sort
 
         plan = []
+        goal_walker = goals[::-1]
+        flattened_goals = []
+        for base_goal in goals[::-1]:
+            goal_walker = [base_goal]
+            dep_walker = [iter(self.tasks[base_goal.lstrip('?')][1])]
+            while goal_walker:
+                for subgoal in dep_walker[-1]:
+                    break
+                else:
+                    # all dependencies are in flattened_goals. record
+                    # this goal.
+                    dep_walker.pop()
+                    goal = goal_walker.pop()
+                    if goal not in flattened_goals:
+                        flattened_goals.append(goal)
+                    continue
+                if subgoal in goal_walker:
+                    raise RuntimeException('circular dependency')
 
-        while True:
-            cands = dict.fromkeys([constr[0] for constr in constraints if 
constr])
-            if not cands:
-                break
+                # subgoal must be at least as optional as its parent
+                qs = goal_walker[-1].count('?')
+                if subgoal.count('?') < qs:
+                    subgoal = '?' * qs + subgoal.lstrip('?')
 
-            for cand in cands:
-                for constr in constraints:
-                    if cand in constr[1:]:
-                        break
-                else:
-                    break
-            else:
-                raise RuntimeError("circular dependecy")
+                # we'll add this goal once we have its dependencies.
+                goal_walker.append(subgoal)
+                dep_walker.append(iter(self.tasks[subgoal.lstrip('?')][1]))
 
-            plan.append(cand)
-            for constr in constraints:
-                if constr and constr[0] == cand:
-                    del constr[0]
-
-        plan.reverse()
-
+        plan = []
+        for name in flattened_goals:
+            name = name.lstrip('?')
+            if name in plan:
+                continue
+            will_run = name in flattened_goals or (
+                        '?' + name in flattened_goals and name not in skip)
+            if will_run:
+                plan.append(name)
         self._plan_cache[key] = plan
-
         return plan
 
     def _depending_on(self, goal):
diff --git a/rpython/translator/tool/test/test_taskengine.py 
b/rpython/translator/tool/test/test_taskengine.py
--- a/rpython/translator/tool/test/test_taskengine.py
+++ b/rpython/translator/tool/test/test_taskengine.py
@@ -148,3 +148,29 @@
     assert drv._plan(['D', 'T', 'R']) == ['A', 'R', 'b', 'H', 'T', 'B', 'D']
     assert drv._plan(['D', 'T']) == ['A', 'R', 'b', 'H', 'T', 'B', 'D']
     assert drv._plan(['D', 'T'], skip=['B']) == ['A', 'R', 'b', 'H', 'T', 'D']
+
+
+def test_can_be_optional():
+    class Drv(SimpleTaskEngine):
+        def task_A():
+            pass
+
+        def task_B():
+            pass
+
+        task_B.task_deps = ['??A']
+
+        def task_C():
+            pass
+
+        task_C.task_deps = ['??B']
+
+        def task_D():
+            pass
+
+        task_D.task_deps = ['B', 'C']
+
+    drv = Drv()
+    assert drv._plan(['D']) == ['B', 'C', 'D']
+    assert drv._plan(['B', 'D']) == ['B', 'C', 'D']
+    assert drv._plan(['A', 'D']) == ['A', 'B', 'C', 'D']
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to