1#!/usr/bin/env python 2# encoding: utf-8 3# Thomas Nagy, 2011 (ita) 4 5""" 6A make-like way of executing the build, following the relationships between inputs/outputs 7 8This algorithm will lead to slower builds, will not be as flexible as "waf build", but 9it might be useful for building data files (?) 10 11It is likely to break in the following cases: 12- files are created dynamically (no inputs or outputs) 13- headers 14- building two files from different groups 15""" 16 17import re 18from waflib import Options, Task 19from waflib.Build import BuildContext 20 21class MakeContext(BuildContext): 22 '''executes tasks in a step-by-step manner, following dependencies between inputs/outputs''' 23 cmd = 'make' 24 fun = 'build' 25 26 def __init__(self, **kw): 27 super(MakeContext, self).__init__(**kw) 28 self.files = Options.options.files 29 30 def get_build_iterator(self): 31 if not self.files: 32 while 1: 33 yield super(MakeContext, self).get_build_iterator() 34 35 for g in self.groups: 36 for tg in g: 37 try: 38 f = tg.post 39 except AttributeError: 40 pass 41 else: 42 f() 43 44 provides = {} 45 uses = {} 46 all_tasks = [] 47 tasks = [] 48 for pat in self.files.split(','): 49 matcher = self.get_matcher(pat) 50 for tg in g: 51 if isinstance(tg, Task.Task): 52 lst = [tg] 53 else: 54 lst = tg.tasks 55 for tsk in lst: 56 all_tasks.append(tsk) 57 58 do_exec = False 59 for node in tsk.inputs: 60 try: 61 uses[node].append(tsk) 62 except: 63 uses[node] = [tsk] 64 65 if matcher(node, output=False): 66 do_exec = True 67 break 68 69 for node in tsk.outputs: 70 try: 71 provides[node].append(tsk) 72 except: 73 provides[node] = [tsk] 74 75 if matcher(node, output=True): 76 do_exec = True 77 break 78 if do_exec: 79 tasks.append(tsk) 80 81 # so we have the tasks that we need to process, the list of all tasks, 82 # the map of the tasks providing nodes, and the map of tasks using nodes 83 84 if not tasks: 85 # if there are no tasks matching, return everything in the current group 86 result = all_tasks 87 else: 88 # this is like a big filter... 89 result = set() 90 seen = set() 91 cur = set(tasks) 92 while cur: 93 result |= cur 94 tosee = set() 95 for tsk in cur: 96 for node in tsk.inputs: 97 if node in seen: 98 continue 99 seen.add(node) 100 tosee |= set(provides.get(node, [])) 101 cur = tosee 102 result = list(result) 103 104 Task.set_file_constraints(result) 105 Task.set_precedence_constraints(result) 106 yield result 107 108 while 1: 109 yield [] 110 111 def get_matcher(self, pat): 112 # this returns a function 113 inn = True 114 out = True 115 if pat.startswith('in:'): 116 out = False 117 pat = pat.replace('in:', '') 118 elif pat.startswith('out:'): 119 inn = False 120 pat = pat.replace('out:', '') 121 122 anode = self.root.find_node(pat) 123 pattern = None 124 if not anode: 125 if not pat.startswith('^'): 126 pat = '^.+?%s' % pat 127 if not pat.endswith('$'): 128 pat = '%s$' % pat 129 pattern = re.compile(pat) 130 131 def match(node, output): 132 if output and not out: 133 return False 134 if not output and not inn: 135 return False 136 137 if anode: 138 return anode == node 139 else: 140 return pattern.match(node.abspath()) 141 return match 142 143