From f49f1628179a9f64436d0bcb43a18003f572c6d1 Mon Sep 17 00:00:00 2001 From: Thomas Nagy Date: Thu, 15 Jun 2017 23:23:48 +0200 Subject: [PATCH] Rework the priority system - Have Task.weight apply to the current task only - Do not rely on object addresses to set the build order - Introduce tg.tg_idx_count to count task generators - Enable propagating/non-propagating weights through Task.tree_weight/Task.weight --- waflib/Runner.py | 15 ++++++++++----- waflib/Task.py | 33 ++++++++++++++++++++++----------- waflib/TaskGen.py | 9 ++++++++- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/waflib/Runner.py b/waflib/Runner.py index 2fb7da3f..c932bdd9 100644 --- a/waflib/Runner.py +++ b/waflib/Runner.py @@ -228,8 +228,6 @@ class Parallel(object): elif not self.count: tasks = next(self.biter) ready, waiting = self.prio_and_split(tasks) - # We cannot use a priority queue because the implementation - # must be able to handle postponed dependencies self.outstanding.extend(ready) self.incomplete.update(waiting) self.total = self.bld.total() @@ -447,6 +445,11 @@ class Parallel(object): dependency cycles are found quickly, and builds should be more efficient. A high priority number means that a task is processed first. + This method can be overridden to disable the priority system:: + + def prio_and_split(self, tasks): + return tasks, [] + :return: A pair of task lists :rtype: tuple """ @@ -476,15 +479,17 @@ class Parallel(object): if n.visited == 0: n.visited = 1 + if n in reverse: rev = reverse[n] - n.__order = n.priority() + len(rev) + sum(visit(k) for k in rev) + n.prio_order = n.tree_weight + len(rev) + sum(visit(k) for k in rev) else: - n.__order = n.priority() + n.prio_order = n.tree_weight + n.visited = 2 elif n.visited == 1: raise Errors.WafError('Dependency cycle found!') - return n.__order + return n.prio_order for x in tasks: if x.visited != 0: diff --git a/waflib/Task.py b/waflib/Task.py index 30a60ec6..40435584 100644 --- a/waflib/Task.py +++ b/waflib/Task.py @@ -153,7 +153,20 @@ class Task(evil): This may be useful for certain extensions but it can a lot of memory. """ - __slots__ = ('hasrun', 'generator', 'env', 'inputs', 'outputs', 'dep_nodes', 'run_after', '__order') + weight = 0 + """Optional weight to tune the priority for task instances. + The higher, the earlier. The weight only applies to single task objects.""" + + tree_weight = 0 + """Optional weight to tune the priority of task instances and whole subtrees. + The higher, the earlier.""" + + prio_order = 0 + """Priority order set by the scheduler on instances during the build phase. + You most likely do not need to set it. + """ + + __slots__ = ('hasrun', 'generator', 'env', 'inputs', 'outputs', 'dep_nodes', 'run_after') def __init__(self, *k, **kw): self.hasrun = NOT_RUN @@ -177,16 +190,14 @@ class Task(evil): self.run_after = set() """Set of tasks that must be executed before this one""" - self.__order = 0 - def __lt__(self, other): - return self.__order > other.__order or id(self) > id(other) + return self.priority() > other.priority() def __le__(self, other): - return self.__order >= other.__order or id(self) >= id(other) + return self.priority() >= other.priority() def __gt__(self, other): - return self.__order < other.__order or id(self) < id(other) + return self.priority() < other.priority() def __ge__(self, other): - return self.__order <= other.__order or id(self) <= id(other) + return self.priority() <= other.priority() def get_cwd(self): """ @@ -222,12 +233,12 @@ class Task(evil): def priority(self): """ - The default priority for this task instance + Priority of execution; the higher, the earlier - :return: a numeric value representing the urgency of running this task (the higher, the sooner) - :rtype: int + :return: the priority value + :rtype: a tuple of numeric values """ - return getattr(self, 'weight', 0) + return (self.weight + self.prio_order, - self.generator.tg_idx_count) def split_argfile(self, cmd): """ diff --git a/waflib/TaskGen.py b/waflib/TaskGen.py index c8d61379..ca098ce2 100644 --- a/waflib/TaskGen.py +++ b/waflib/TaskGen.py @@ -76,13 +76,20 @@ class task_gen(object): self.env = self.bld.env.derive() self.path = self.bld.path # emulate chdir when reading scripts - # provide a unique id + # Provide a unique index per folder + # This is part of a measure to prevent output file name collisions try: self.idx = self.bld.idx[self.path] = self.bld.idx.get(self.path, 0) + 1 except AttributeError: self.bld.idx = {} self.idx = self.bld.idx[self.path] = 1 + # Record the global task generator count + try: + self.tg_idx_count = self.bld.tg_idx_count = self.bld.tg_idx_count + 1 + except AttributeError: + self.tg_idx_count = self.bld.tg_idx_count = 1 + for key, val in kw.items(): setattr(self, key, val)