3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Thomas Nagy, 2008-2010 (ita)
10 Execute the tasks with gcc -MD, read the dependencies from the .d file
11 and prepare the dependency calculation for the next run.
12 This affects the cxx class, so make sure to load Qt5 after this tool.
19 import os, re, threading
20 from waflib import Task, Logs, Utils, Errors
21 from waflib.Tools import c_preproc
22 from waflib.TaskGen import before_method, feature
24 lock = threading.Lock()
26 gccdeps_flags = ['-MD']
27 if not c_preproc.go_absolute:
28 gccdeps_flags = ['-MMD']
30 # Third-party tools are allowed to add extra names in here with append()
31 supported_compilers = ['gcc', 'icc', 'clang']
34 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
35 return super(self.derived_gccdeps, self).scan()
36 nodes = self.generator.bld.node_deps.get(self.uid(), [])
40 re_o = re.compile("\.o$")
41 re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
43 def remove_makefile_rule_lhs(line):
44 # Splitting on a plain colon would accidentally match inside a
45 # Windows absolute-path filename, so we must search for a colon
46 # followed by whitespace to find the divider between LHS and RHS
47 # of the Makefile rule.
50 sep_idx = line.find(rulesep)
52 return line[sep_idx + 2:]
56 def path_to_node(base_node, path, cached_nodes):
57 # Take the base node and the path and return a node
58 # Results are cached because searching the node tree is expensive
59 # The following code is executed by threads, it is not safe, so a lock is needed...
60 if getattr(path, '__hash__'):
61 node_lookup_key = (base_node, path)
63 # Not hashable, assume it is a list and join into a string
64 node_lookup_key = (base_node, os.path.sep.join(path))
67 node = cached_nodes[node_lookup_key]
69 node = base_node.find_resource(path)
70 cached_nodes[node_lookup_key] = node
76 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
77 return super(self.derived_gccdeps, self).post_run()
79 name = self.outputs[0].abspath()
80 name = re_o.sub('.d', name)
82 txt = Utils.readf(name)
83 except EnvironmentError:
84 Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
88 # Compilers have the choice to either output the file's dependencies
89 # as one large Makefile rule:
91 # /path/to/file.o: /path/to/dep1.h \
96 # or as many individual rules:
98 # /path/to/file.o: /path/to/dep1.h
99 # /path/to/file.o: /path/to/dep2.h
100 # /path/to/file.o: /path/to/dep3.h
103 # So the first step is to sanitize the input by stripping out the left-
104 # hand side of all these lines. After that, whatever remains are the
105 # implicit dependencies of task.outputs[0]
106 txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
108 # Now join all the lines together
109 txt = txt.replace('\\\n', '')
112 val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
115 bld = self.generator.bld
117 # Dynamically bind to the cache
119 cached_nodes = bld.cached_nodes
120 except AttributeError:
121 cached_nodes = bld.cached_nodes = {}
127 node = path_to_node(bld.root, x, cached_nodes)
129 # TODO waf 1.9 - single cwd value
130 path = getattr(bld, 'cwdx', bld.bldnode)
131 # when calling find_resource, make sure the path does not contain '..'
132 x = [k for k in Utils.split_path(x) if k and k != '.']
142 node = path_to_node(path, x, cached_nodes)
145 raise ValueError('could not find %r for %r' % (x, self))
146 if id(node) == id(self.inputs[0]):
147 # ignore the source file, it is already in the dependencies
148 # this way, successful config tests may be retrieved from the cache
152 Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
154 bld.node_deps[self.uid()] = nodes
155 bld.raw_deps[self.uid()] = []
159 except AttributeError:
162 Task.Task.post_run(self)
164 def sig_implicit_deps(self):
165 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
166 return super(self.derived_gccdeps, self).sig_implicit_deps()
168 return Task.Task.sig_implicit_deps(self)
169 except Errors.WafError:
172 def wrap_compiled_task(classname):
173 derived_class = type(classname, (Task.classes[classname],), {})
174 derived_class.derived_gccdeps = derived_class
175 derived_class.post_run = post_run
176 derived_class.scan = scan
177 derived_class.sig_implicit_deps = sig_implicit_deps
179 for k in ('c', 'cxx'):
180 if k in Task.classes:
181 wrap_compiled_task(k)
183 @before_method('process_source')
184 @feature('force_gccdeps')
185 def force_gccdeps(self):
186 self.env.ENABLE_GCCDEPS = ['c', 'cxx']
189 # in case someone provides a --enable-gccdeps command-line option
190 if not getattr(conf.options, 'enable_gccdeps', True):
194 flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
195 if conf.env.CC_NAME in supported_compilers:
197 conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
198 except Errors.ConfigurationError:
201 conf.env.append_value('CFLAGS', gccdeps_flags)
202 conf.env.append_unique('ENABLE_GCCDEPS', 'c')
204 if conf.env.CXX_NAME in supported_compilers:
206 conf.check(fragment='int main() { return 0; }', features='cxx force_gccdeps', cxxflags=flags, msg='Checking for cxx flags %r' % ''.join(flags))
207 except Errors.ConfigurationError:
210 conf.env.append_value('CXXFLAGS', gccdeps_flags)
211 conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')