make.py 3.1 KB

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