#!/usr/bin/env python # encoding: utf-8 # Thomas Nagy, 2011 (ita) """ A make-like way of executing the build, following the relationships between inputs/outputs This algorithm will lead to slower builds, will not be as flexible as "waf build", but it might be useful for building data files (?) It is likely to break in the following cases: - files are created dynamically (no inputs or outputs) - headers - building two files from different groups """ import re from waflib import Options, Task from waflib.Build import BuildContext class MakeContext(BuildContext): '''executes tasks in a step-by-step manner, following dependencies between inputs/outputs''' cmd = 'make' fun = 'build' def __init__(self, **kw): super(MakeContext, self).__init__(**kw) self.files = Options.options.files def get_build_iterator(self): if not self.files: while 1: yield super(MakeContext, self).get_build_iterator() for g in self.groups: for tg in g: try: f = tg.post except AttributeError: pass else: f() provides = {} uses = {} all_tasks = [] tasks = [] for pat in self.files.split(','): matcher = self.get_matcher(pat) for tg in g: if isinstance(tg, Task.Task): lst = [tg] else: lst = tg.tasks for tsk in lst: all_tasks.append(tsk) do_exec = False for node in tsk.inputs: try: uses[node].append(tsk) except: uses[node] = [tsk] if matcher(node, output=False): do_exec = True break for node in tsk.outputs: try: provides[node].append(tsk) except: provides[node] = [tsk] if matcher(node, output=True): do_exec = True break if do_exec: tasks.append(tsk) # so we have the tasks that we need to process, the list of all tasks, # the map of the tasks providing nodes, and the map of tasks using nodes if not tasks: # if there are no tasks matching, return everything in the current group result = all_tasks else: # this is like a big filter... result = set() seen = set() cur = set(tasks) while cur: result |= cur tosee = set() for tsk in cur: for node in tsk.inputs: if node in seen: continue seen.add(node) tosee |= set(provides.get(node, [])) cur = tosee result = list(result) Task.set_file_constraints(result) Task.set_precedence_constraints(result) yield result while 1: yield [] def get_matcher(self, pat): # this returns a function inn = True out = True if pat.startswith('in:'): out = False pat = pat.replace('in:', '') elif pat.startswith('out:'): inn = False pat = pat.replace('out:', '') anode = self.root.find_node(pat) pattern = None if not anode: if not pat.startswith('^'): pat = '^.+?%s' % pat if not pat.endswith('$'): pat = '%s$' % pat pattern = re.compile(pat) def match(node, output): if output and not out: return False if not output and not inn: return False if anode: return anode == node else: return pattern.match(node.abspath()) return match