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.
17 opt.load('compiler_cxx')
19 conf.load('compiler_cxx gccdeps')
22 import os, re, threading
23 from waflib import Task, Logs, Utils, Errors
24 from waflib.Tools import c_preproc
25 from waflib.TaskGen import before_method, feature
27 lock = threading.Lock()
29 gccdeps_flags = ['-MD']
30 if not c_preproc.go_absolute:
31 gccdeps_flags = ['-MMD']
33 # Third-party tools are allowed to add extra names in here with append()
34 supported_compilers = ['gcc', 'icc', 'clang']
37 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
38 return super(self.derived_gccdeps, self).scan()
39 nodes = self.generator.bld.node_deps.get(self.uid(), [])
43 re_o = re.compile("\.o$")
44 re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
46 def remove_makefile_rule_lhs(line):
47 # Splitting on a plain colon would accidentally match inside a
48 # Windows absolute-path filename, so we must search for a colon
49 # followed by whitespace to find the divider between LHS and RHS
50 # of the Makefile rule.
53 sep_idx = line.find(rulesep)
55 return line[sep_idx + 2:]
59 def path_to_node(base_node, path, cached_nodes):
60 # Take the base node and the path and return a node
61 # Results are cached because searching the node tree is expensive
62 # The following code is executed by threads, it is not safe, so a lock is needed...
63 if getattr(path, '__hash__'):
64 node_lookup_key = (base_node, path)
66 # Not hashable, assume it is a list and join into a string
67 node_lookup_key = (base_node, os.path.sep.join(path))
70 node = cached_nodes[node_lookup_key]
72 node = base_node.find_resource(path)
73 cached_nodes[node_lookup_key] = node
79 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
80 return super(self.derived_gccdeps, self).post_run()
82 name = self.outputs[0].abspath()
83 name = re_o.sub('.d', name)
85 txt = Utils.readf(name)
86 except EnvironmentError:
87 Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
91 # Compilers have the choice to either output the file's dependencies
92 # as one large Makefile rule:
94 # /path/to/file.o: /path/to/dep1.h \
99 # or as many individual rules:
101 # /path/to/file.o: /path/to/dep1.h
102 # /path/to/file.o: /path/to/dep2.h
103 # /path/to/file.o: /path/to/dep3.h
106 # So the first step is to sanitize the input by stripping out the left-
107 # hand side of all these lines. After that, whatever remains are the
108 # implicit dependencies of task.outputs[0]
109 txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
111 # Now join all the lines together
112 txt = txt.replace('\\\n', '')
115 val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
118 bld = self.generator.bld
120 # Dynamically bind to the cache
122 cached_nodes = bld.cached_nodes
123 except AttributeError:
124 cached_nodes = bld.cached_nodes = {}
130 node = path_to_node(bld.root, x, cached_nodes)
132 # TODO waf 1.9 - single cwd value
133 path = getattr(bld, 'cwdx', bld.bldnode)
134 # when calling find_resource, make sure the path does not contain '..'
135 x = [k for k in Utils.split_path(x) if k and k != '.']
145 node = path_to_node(path, x, cached_nodes)
148 raise ValueError('could not find %r for %r' % (x, self))
149 if id(node) == id(self.inputs[0]):
150 # ignore the source file, it is already in the dependencies
151 # this way, successful config tests may be retrieved from the cache
155 Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
157 bld.node_deps[self.uid()] = nodes
158 bld.raw_deps[self.uid()] = []
162 except AttributeError:
165 Task.Task.post_run(self)
167 def sig_implicit_deps(self):
168 if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
169 return super(self.derived_gccdeps, self).sig_implicit_deps()
171 return Task.Task.sig_implicit_deps(self)
172 except Errors.WafError:
175 def wrap_compiled_task(classname):
176 derived_class = type(classname, (Task.classes[classname],), {})
177 derived_class.derived_gccdeps = derived_class
178 derived_class.post_run = post_run
179 derived_class.scan = scan
180 derived_class.sig_implicit_deps = sig_implicit_deps
182 for k in ('c', 'cxx'):
183 if k in Task.classes:
184 wrap_compiled_task(k)
186 @before_method('process_source')
187 @feature('force_gccdeps')
188 def force_gccdeps(self):
189 self.env.ENABLE_GCCDEPS = ['c', 'cxx']
192 # in case someone provides a --enable-gccdeps command-line option
193 if not getattr(conf.options, 'enable_gccdeps', True):
197 flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
198 if conf.env.CC_NAME in supported_compilers:
200 conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
201 except Errors.ConfigurationError:
204 conf.env.append_value('CFLAGS', gccdeps_flags)
205 conf.env.append_unique('ENABLE_GCCDEPS', 'c')
207 if conf.env.CXX_NAME in supported_compilers:
209 conf.check(fragment='int main() { return 0; }', features='cxx force_gccdeps', cxxflags=flags, msg='Checking for cxx flags %r' % ''.join(flags))
210 except Errors.ConfigurationError:
213 conf.env.append_value('CXXFLAGS', gccdeps_flags)
214 conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')
217 raise ValueError('Do not load gccdeps options')