text: #!/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, Logs
  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.TaskBase):
  						lst = [tg]
  					else:
  						lst = tg.tasks
  					for tsk in lst:
  						all_tasks.append(tsk)
  
  						do_exec = False
  						for node in getattr(tsk, 'inputs', []):
  							try:
  								uses[node].append(tsk)
  							except:
  								uses[node] = [tsk]
  
  							if matcher(node, output=False):
  								do_exec = True
  								break
  
  						for node in getattr(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 getattr(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 == True and not out:
  				return False
  			if output == False and not inn:
  				return False
  
  			if anode:
  				return anode == node
  			else:
  				return pattern.match(node.abspath())
  		return match
  
  