diff options
author | David Robillard <d@drobilla.net> | 2018-09-15 14:51:03 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2018-09-15 14:51:03 +0200 |
commit | c2b0355b0de23f6ec83b5fa5be038bcce208e26f (patch) | |
tree | ff0760d5f3f0e994413c8c1dff7a4fed37914bee /extras/make.py | |
download | serd-c2b0355b0de23f6ec83b5fa5be038bcce208e26f.tar.gz serd-c2b0355b0de23f6ec83b5fa5be038bcce208e26f.tar.bz2 serd-c2b0355b0de23f6ec83b5fa5be038bcce208e26f.zip |
Squashed 'waflib/' content from commit 982416b
git-subtree-dir: waflib
git-subtree-split: 982416b8a6c6728e200243e1be3ab60435c08830
Diffstat (limited to 'extras/make.py')
-rw-r--r-- | extras/make.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/extras/make.py b/extras/make.py new file mode 100644 index 00000000..933d9cac --- /dev/null +++ b/extras/make.py @@ -0,0 +1,142 @@ +#!/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 + |