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
= ['gas', 'gcc', 'icc', 'clang']
32 re_o
= re
.compile(r
"\.o$")
33 re_splitter
= re
.compile(r
'(?<!\\)\s+') # split by space, except when spaces are escaped
35 def remove_makefile_rule_lhs(line
):
36 # Splitting on a plain colon would accidentally match inside a
37 # Windows absolute-path filename, so we must search for a colon
38 # followed by whitespace to find the divider between LHS and RHS
39 # of the Makefile rule.
42 sep_idx
= line
.find(rulesep
)
44 return line
[sep_idx
+ 2:]
48 def path_to_node(base_node
, path
, cached_nodes
):
49 # Take the base node and the path and return a node
50 # Results are cached because searching the node tree is expensive
51 # The following code is executed by threads, it is not safe, so a lock is needed...
52 if getattr(path
, '__hash__'):
53 node_lookup_key
= (base_node
, path
)
55 # Not hashable, assume it is a list and join into a string
56 node_lookup_key
= (base_node
, os
.path
.sep
.join(path
))
59 node
= cached_nodes
[node_lookup_key
]
61 # retry with lock on cache miss
64 node
= cached_nodes
[node_lookup_key
]
66 node
= cached_nodes
[node_lookup_key
] = base_node
.find_resource(path
)
71 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
72 return super(self
.derived_gccdeps
, self
).post_run()
74 deps_filename
= self
.outputs
[0].abspath()
75 deps_filename
= re_o
.sub('.d', deps_filename
)
77 deps_txt
= Utils
.readf(deps_filename
)
78 except EnvironmentError:
79 Logs
.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
82 # Compilers have the choice to either output the file's dependencies
83 # as one large Makefile rule:
85 # /path/to/file.o: /path/to/dep1.h \
90 # or as many individual rules:
92 # /path/to/file.o: /path/to/dep1.h
93 # /path/to/file.o: /path/to/dep2.h
94 # /path/to/file.o: /path/to/dep3.h
97 # So the first step is to sanitize the input by stripping out the left-
98 # hand side of all these lines. After that, whatever remains are the
99 # implicit dependencies of task.outputs[0]
100 deps_txt
= '\n'.join([remove_makefile_rule_lhs(line
) for line
in deps_txt
.splitlines()])
102 # Now join all the lines together
103 deps_txt
= deps_txt
.replace('\\\n', '')
105 dep_paths
= deps_txt
.strip()
106 dep_paths
= [x
.replace('\\ ', ' ') for x
in re_splitter
.split(dep_paths
) if x
]
109 unresolved_names
= []
110 bld
= self
.generator
.bld
112 # Dynamically bind to the cache
114 cached_nodes
= bld
.cached_nodes
115 except AttributeError:
116 cached_nodes
= bld
.cached_nodes
= {}
118 for path
in dep_paths
:
121 if os
.path
.isabs(path
):
122 node
= path_to_node(bld
.root
, path
, cached_nodes
)
124 # TODO waf 1.9 - single cwd value
125 base_node
= getattr(bld
, 'cwdx', bld
.bldnode
)
126 # when calling find_resource, make sure the path does not contain '..'
127 path
= [k
for k
in Utils
.split_path(path
) if k
and k
!= '.']
129 idx
= path
.index('..')
132 base_node
= base_node
.parent
137 node
= path_to_node(base_node
, path
, cached_nodes
)
140 raise ValueError('could not find %r for %r' % (path
, self
))
142 if id(node
) == id(self
.inputs
[0]):
143 # ignore the source file, it is already in the dependencies
144 # this way, successful config tests may be retrieved from the cache
147 resolved_nodes
.append(node
)
149 Logs
.debug('deps: gccdeps for %s returned %s', self
, resolved_nodes
)
151 bld
.node_deps
[self
.uid()] = resolved_nodes
152 bld
.raw_deps
[self
.uid()] = unresolved_names
156 except AttributeError:
159 Task
.Task
.post_run(self
)
162 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
163 return super(self
.derived_gccdeps
, self
).scan()
165 resolved_nodes
= self
.generator
.bld
.node_deps
.get(self
.uid(), [])
166 unresolved_names
= []
167 return (resolved_nodes
, unresolved_names
)
169 def sig_implicit_deps(self
):
170 if not self
.__class
__.__name
__ in self
.env
.ENABLE_GCCDEPS
:
171 return super(self
.derived_gccdeps
, self
).sig_implicit_deps()
172 bld
= self
.generator
.bld
175 return self
.compute_sig_implicit_deps()
176 except Errors
.TaskNotReady
:
177 raise ValueError("Please specify the build order precisely with gccdeps (asm/c/c++ tasks)")
178 except EnvironmentError:
179 # If a file is renamed, assume the dependencies are stale and must be recalculated
180 for x
in bld
.node_deps
.get(self
.uid(), []):
181 if not x
.is_bld() and not x
.exists():
183 del x
.parent
.children
[x
.name
]
188 bld
.node_deps
[key
] = []
189 bld
.raw_deps
[key
] = []
192 def wrap_compiled_task(classname
):
193 derived_class
= type(classname
, (Task
.classes
[classname
],), {})
194 derived_class
.derived_gccdeps
= derived_class
195 derived_class
.post_run
= post_run
196 derived_class
.scan
= scan
197 derived_class
.sig_implicit_deps
= sig_implicit_deps
199 for k
in ('asm', 'c', 'cxx'):
200 if k
in Task
.classes
:
201 wrap_compiled_task(k
)
203 @before_method('process_source')
204 @feature('force_gccdeps')
205 def force_gccdeps(self
):
206 self
.env
.ENABLE_GCCDEPS
= ['asm', 'c', 'cxx']
209 # in case someone provides a --enable-gccdeps command-line option
210 if not getattr(conf
.options
, 'enable_gccdeps', True):
214 flags
= conf
.env
.GCCDEPS_FLAGS
or gccdeps_flags
215 if conf
.env
.ASM_NAME
in supported_compilers
:
217 conf
.check(fragment
='', features
='asm force_gccdeps', asflags
=flags
, compile_filename
='test.S', msg
='Checking for asm flags %r' % ''.join(flags
))
218 except Errors
.ConfigurationError
:
221 conf
.env
.append_value('ASFLAGS', flags
)
222 conf
.env
.append_unique('ENABLE_GCCDEPS', 'asm')
224 if conf
.env
.CC_NAME
in supported_compilers
:
226 conf
.check(fragment
='int main() { return 0; }', features
='c force_gccdeps', cflags
=flags
, msg
='Checking for c flags %r' % ''.join(flags
))
227 except Errors
.ConfigurationError
:
230 conf
.env
.append_value('CFLAGS', flags
)
231 conf
.env
.append_unique('ENABLE_GCCDEPS', 'c')
233 if conf
.env
.CXX_NAME
in supported_compilers
:
235 conf
.check(fragment
='int main() { return 0; }', features
='cxx force_gccdeps', cxxflags
=flags
, msg
='Checking for cxx flags %r' % ''.join(flags
))
236 except Errors
.ConfigurationError
:
239 conf
.env
.append_value('CXXFLAGS', flags
)
240 conf
.env
.append_unique('ENABLE_GCCDEPS', 'cxx')
243 raise ValueError('Do not load gccdeps options')