3 # Thomas Nagy, 2008-2010 (ita)
6 Execute the tasks with gcc -MD, read the dependencies from the .d file
7 and prepare the dependency calculation for the next run.
8 This affects the cxx class, so make sure to load Qt5 after this tool.
13 opt.load('compiler_cxx')
15 conf.load('compiler_cxx gccdeps')
18 import os
, re
, threading
19 from waflib
import Task
, Logs
, Utils
, Errors
20 from waflib
.Tools
import c_preproc
21 from waflib
.TaskGen
import before_method
, feature
23 lock
= threading
.Lock()
25 gccdeps_flags
= ['-MD']
26 if not c_preproc
.go_absolute
:
27 gccdeps_flags
= ['-MMD']
29 # Third-party tools are allowed to add extra names in here with append()
30 supported_compilers
= ['gcc', 'icc', 'clang']
33 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
34 return super(self
.derived_gccdeps
, self
).scan()
35 nodes
= self
.generator
.bld
.node_deps
.get(self
.uid(), [])
39 re_o
= re
.compile("\.o$")
40 re_splitter
= re
.compile(r
'(?<!\\)\s+') # split by space, except when spaces are escaped
42 def remove_makefile_rule_lhs(line
):
43 # Splitting on a plain colon would accidentally match inside a
44 # Windows absolute-path filename, so we must search for a colon
45 # followed by whitespace to find the divider between LHS and RHS
46 # of the Makefile rule.
49 sep_idx
= line
.find(rulesep
)
51 return line
[sep_idx
+ 2:]
55 def path_to_node(base_node
, path
, cached_nodes
):
56 # Take the base node and the path and return a node
57 # Results are cached because searching the node tree is expensive
58 # The following code is executed by threads, it is not safe, so a lock is needed...
59 if getattr(path
, '__hash__'):
60 node_lookup_key
= (base_node
, path
)
62 # Not hashable, assume it is a list and join into a string
63 node_lookup_key
= (base_node
, os
.path
.sep
.join(path
))
66 node
= cached_nodes
[node_lookup_key
]
68 node
= base_node
.find_resource(path
)
69 cached_nodes
[node_lookup_key
] = node
75 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
76 return super(self
.derived_gccdeps
, self
).post_run()
78 name
= self
.outputs
[0].abspath()
79 name
= re_o
.sub('.d', name
)
81 txt
= Utils
.readf(name
)
82 except EnvironmentError:
83 Logs
.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
87 # Compilers have the choice to either output the file's dependencies
88 # as one large Makefile rule:
90 # /path/to/file.o: /path/to/dep1.h \
95 # or as many individual rules:
97 # /path/to/file.o: /path/to/dep1.h
98 # /path/to/file.o: /path/to/dep2.h
99 # /path/to/file.o: /path/to/dep3.h
102 # So the first step is to sanitize the input by stripping out the left-
103 # hand side of all these lines. After that, whatever remains are the
104 # implicit dependencies of task.outputs[0]
105 txt
= '\n'.join([remove_makefile_rule_lhs(line
) for line
in txt
.splitlines()])
107 # Now join all the lines together
108 txt
= txt
.replace('\\\n', '')
111 val
= [x
.replace('\\ ', ' ') for x
in re_splitter
.split(val
) if x
]
114 bld
= self
.generator
.bld
116 # Dynamically bind to the cache
118 cached_nodes
= bld
.cached_nodes
119 except AttributeError:
120 cached_nodes
= bld
.cached_nodes
= {}
126 node
= path_to_node(bld
.root
, x
, cached_nodes
)
128 # TODO waf 1.9 - single cwd value
129 path
= getattr(bld
, 'cwdx', bld
.bldnode
)
130 # when calling find_resource, make sure the path does not contain '..'
131 x
= [k
for k
in Utils
.split_path(x
) if k
and k
!= '.']
141 node
= path_to_node(path
, x
, cached_nodes
)
144 raise ValueError('could not find %r for %r' % (x
, self
))
145 if id(node
) == id(self
.inputs
[0]):
146 # ignore the source file, it is already in the dependencies
147 # this way, successful config tests may be retrieved from the cache
151 Logs
.debug('deps: gccdeps for %s returned %s', self
, nodes
)
153 bld
.node_deps
[self
.uid()] = nodes
154 bld
.raw_deps
[self
.uid()] = []
158 except AttributeError:
161 Task
.Task
.post_run(self
)
163 def sig_implicit_deps(self
):
164 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
165 return super(self
.derived_gccdeps
, self
).sig_implicit_deps()
167 return Task
.Task
.sig_implicit_deps(self
)
168 except Errors
.WafError
:
171 def wrap_compiled_task(classname
):
172 derived_class
= type(classname
, (Task
.classes
[classname
],), {})
173 derived_class
.derived_gccdeps
= derived_class
174 derived_class
.post_run
= post_run
175 derived_class
.scan
= scan
176 derived_class
.sig_implicit_deps
= sig_implicit_deps
178 for k
in ('c', 'cxx'):
179 if k
in Task
.classes
:
180 wrap_compiled_task(k
)
182 @before_method('process_source')
183 @feature('force_gccdeps')
184 def force_gccdeps(self
):
185 self
.env
.ENABLE_GCCDEPS
= ['c', 'cxx']
188 # in case someone provides a --enable-gccdeps command-line option
189 if not getattr(conf
.options
, 'enable_gccdeps', True):
193 flags
= conf
.env
.GCCDEPS_FLAGS
or gccdeps_flags
194 if conf
.env
.CC_NAME
in supported_compilers
:
196 conf
.check(fragment
='int main() { return 0; }', features
='c force_gccdeps', cflags
=flags
, msg
='Checking for c flags %r' % ''.join(flags
))
197 except Errors
.ConfigurationError
:
200 conf
.env
.append_value('CFLAGS', gccdeps_flags
)
201 conf
.env
.append_unique('ENABLE_GCCDEPS', 'c')
203 if conf
.env
.CXX_NAME
in supported_compilers
:
205 conf
.check(fragment
='int main() { return 0; }', features
='cxx force_gccdeps', cxxflags
=flags
, msg
='Checking for cxx flags %r' % ''.join(flags
))
206 except Errors
.ConfigurationError
:
209 conf
.env
.append_value('CXXFLAGS', gccdeps_flags
)
210 conf
.env
.append_unique('ENABLE_GCCDEPS', 'cxx')
213 raise ValueError('Do not load gccdeps options')