jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
5.8KB

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2008-2010 (ita)
  4. """
  5. Execute the tasks with gcc -MD, read the dependencies from the .d file
  6. and prepare the dependency calculation for the next run.
  7. Usage:
  8. def configure(conf):
  9. conf.load('gccdeps')
  10. """
  11. import os, re, threading
  12. from waflib import Task, Logs, Utils, Errors
  13. from waflib.Tools import c_preproc
  14. from waflib.TaskGen import before_method, feature
  15. lock = threading.Lock()
  16. gccdeps_flags = ['-MD']
  17. if not c_preproc.go_absolute:
  18. gccdeps_flags = ['-MMD']
  19. # Third-party tools are allowed to add extra names in here with append()
  20. supported_compilers = ['gcc', 'icc', 'clang']
  21. def scan(self):
  22. if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
  23. if not self.env.GCCDEPS:
  24. self.generator.bld.fatal('Load gccdeps in configure!')
  25. return self.no_gccdeps_scan()
  26. nodes = self.generator.bld.node_deps.get(self.uid(), [])
  27. names = []
  28. return (nodes, names)
  29. re_o = re.compile("\.o$")
  30. re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
  31. def remove_makefile_rule_lhs(line):
  32. # Splitting on a plain colon would accidentally match inside a
  33. # Windows absolute-path filename, so we must search for a colon
  34. # followed by whitespace to find the divider between LHS and RHS
  35. # of the Makefile rule.
  36. rulesep = ': '
  37. sep_idx = line.find(rulesep)
  38. if sep_idx >= 0:
  39. return line[sep_idx + 2:]
  40. else:
  41. return line
  42. def path_to_node(base_node, path, cached_nodes):
  43. # Take the base node and the path and return a node
  44. # Results are cached because searching the node tree is expensive
  45. # The following code is executed by threads, it is not safe, so a lock is needed...
  46. if getattr(path, '__hash__'):
  47. node_lookup_key = (base_node, path)
  48. else:
  49. # Not hashable, assume it is a list and join into a string
  50. node_lookup_key = (base_node, os.path.sep.join(path))
  51. try:
  52. lock.acquire()
  53. node = cached_nodes[node_lookup_key]
  54. except KeyError:
  55. node = base_node.find_resource(path)
  56. cached_nodes[node_lookup_key] = node
  57. finally:
  58. lock.release()
  59. return node
  60. def post_run(self):
  61. # The following code is executed by threads, it is not safe, so a lock is needed...
  62. if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
  63. return self.no_gccdeps_post_run()
  64. if getattr(self, 'cached', None):
  65. return Task.Task.post_run(self)
  66. name = self.outputs[0].abspath()
  67. name = re_o.sub('.d', name)
  68. txt = Utils.readf(name)
  69. #os.remove(name)
  70. # Compilers have the choice to either output the file's dependencies
  71. # as one large Makefile rule:
  72. #
  73. # /path/to/file.o: /path/to/dep1.h \
  74. # /path/to/dep2.h \
  75. # /path/to/dep3.h \
  76. # ...
  77. #
  78. # or as many individual rules:
  79. #
  80. # /path/to/file.o: /path/to/dep1.h
  81. # /path/to/file.o: /path/to/dep2.h
  82. # /path/to/file.o: /path/to/dep3.h
  83. # ...
  84. #
  85. # So the first step is to sanitize the input by stripping out the left-
  86. # hand side of all these lines. After that, whatever remains are the
  87. # implicit dependencies of task.outputs[0]
  88. txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
  89. # Now join all the lines together
  90. txt = txt.replace('\\\n', '')
  91. val = txt.strip()
  92. lst = val.split(':')
  93. val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
  94. nodes = []
  95. bld = self.generator.bld
  96. # Dynamically bind to the cache
  97. try:
  98. cached_nodes = bld.cached_nodes
  99. except AttributeError:
  100. cached_nodes = bld.cached_nodes = {}
  101. for x in val:
  102. node = None
  103. if os.path.isabs(x):
  104. node = path_to_node(bld.root, x, cached_nodes)
  105. else:
  106. path = bld.bldnode
  107. # when calling find_resource, make sure the path does not begin by '..'
  108. x = [k for k in Utils.split_path(x) if k and k != '.']
  109. while lst and x[0] == '..':
  110. x = x[1:]
  111. path = path.parent
  112. node = path_to_node(path, x, cached_nodes)
  113. if not node:
  114. raise ValueError('could not find %r for %r' % (x, self))
  115. if id(node) == id(self.inputs[0]):
  116. # ignore the source file, it is already in the dependencies
  117. # this way, successful config tests may be retrieved from the cache
  118. continue
  119. nodes.append(node)
  120. Logs.debug('deps: gccdeps for %s returned %s' % (str(self), str(nodes)))
  121. bld.node_deps[self.uid()] = nodes
  122. bld.raw_deps[self.uid()] = []
  123. try:
  124. del self.cache_sig
  125. except:
  126. pass
  127. Task.Task.post_run(self)
  128. def sig_implicit_deps(self):
  129. if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
  130. return self.no_gccdeps_sig_implicit_deps()
  131. try:
  132. return Task.Task.sig_implicit_deps(self)
  133. except Errors.WafError:
  134. return Utils.SIG_NIL
  135. for name in 'c cxx'.split():
  136. try:
  137. cls = Task.classes[name]
  138. except KeyError:
  139. pass
  140. else:
  141. cls.no_gccdeps_scan = cls.scan
  142. cls.no_gccdeps_post_run = cls.post_run
  143. cls.no_gccdeps_sig_implicit_deps = cls.sig_implicit_deps
  144. cls.scan = scan
  145. cls.post_run = post_run
  146. cls.sig_implicit_deps = sig_implicit_deps
  147. @before_method('process_source')
  148. @feature('force_gccdeps')
  149. def force_gccdeps(self):
  150. self.env.ENABLE_GCCDEPS = ['c', 'cxx']
  151. def configure(conf):
  152. # record that the configuration was executed properly
  153. conf.env.GCCDEPS = True
  154. global gccdeps_flags
  155. flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
  156. if conf.env.CC_NAME in supported_compilers:
  157. try:
  158. conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
  159. except Errors.ConfigurationError:
  160. pass
  161. else:
  162. conf.env.append_value('CFLAGS', gccdeps_flags)
  163. conf.env.append_unique('ENABLE_GCCDEPS', 'c')
  164. if conf.env.CXX_NAME in supported_compilers:
  165. try:
  166. conf.check(fragment='int main() { return 0; }', features='cxx force_gccdeps', cxxflags=flags, msg='Checking for cxx flags %r' % ''.join(flags))
  167. except Errors.ConfigurationError:
  168. pass
  169. else:
  170. conf.env.append_value('CXXFLAGS', gccdeps_flags)
  171. conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')