third_party: Update waf to version 2.0.25
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / gccdeps.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2008-2010 (ita)
4
5 """
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.
9
10 Usage::
11
12         def options(opt):
13                 opt.load('compiler_cxx')
14         def configure(conf):
15                 conf.load('compiler_cxx gccdeps')
16 """
17
18 import os, re, threading
19 from waflib import Task, Logs, Utils, Errors
20 from waflib.Tools import asm, c, c_preproc, cxx
21 from waflib.TaskGen import before_method, feature
22
23 lock = threading.Lock()
24
25 gccdeps_flags = ['-MD']
26 if not c_preproc.go_absolute:
27         gccdeps_flags = ['-MMD']
28
29 # Third-party tools are allowed to add extra names in here with append()
30 supported_compilers = ['gas', 'gcc', 'icc', 'clang']
31
32 re_o = re.compile(r"\.o$")
33 re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
34
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.
40         rulesep = ': '
41
42         sep_idx = line.find(rulesep)
43         if sep_idx >= 0:
44                 return line[sep_idx + 2:]
45         else:
46                 return line
47
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)
54         else:
55                 # Not hashable, assume it is a list and join into a string
56                 node_lookup_key = (base_node, os.path.sep.join(path))
57
58         try:
59                 node = cached_nodes[node_lookup_key]
60         except KeyError:
61                 # retry with lock on cache miss
62                 with lock:
63                         try:
64                                 node = cached_nodes[node_lookup_key]
65                         except KeyError:
66                                 node = cached_nodes[node_lookup_key] = base_node.find_resource(path)
67
68         return node
69
70 def post_run(self):
71         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
72                 return super(self.derived_gccdeps, self).post_run()
73
74         deps_filename = self.outputs[0].abspath()
75         deps_filename = re_o.sub('.d', deps_filename)
76         try:
77                 deps_txt = Utils.readf(deps_filename)
78         except EnvironmentError:
79                 Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
80                 raise
81
82         # Compilers have the choice to either output the file's dependencies
83         # as one large Makefile rule:
84         #
85         #   /path/to/file.o: /path/to/dep1.h \
86         #                    /path/to/dep2.h \
87         #                    /path/to/dep3.h \
88         #                    ...
89         #
90         # or as many individual rules:
91         #
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
95         #   ...
96         #
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()])
101
102         # Now join all the lines together
103         deps_txt = deps_txt.replace('\\\n', '')
104
105         dep_paths = deps_txt.strip()
106         dep_paths = [x.replace('\\ ', ' ') for x in re_splitter.split(dep_paths) if x]
107
108         resolved_nodes = []
109         unresolved_names = []
110         bld = self.generator.bld
111
112         # Dynamically bind to the cache
113         try:
114                 cached_nodes = bld.cached_nodes
115         except AttributeError:
116                 cached_nodes = bld.cached_nodes = {}
117
118         for path in dep_paths:
119
120                 node = None
121                 if os.path.isabs(path):
122                         node = path_to_node(bld.root, path, cached_nodes)
123                 else:
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 != '.']
128                         while '..' in path:
129                                 idx = path.index('..')
130                                 if idx == 0:
131                                         path = path[1:]
132                                         base_node = base_node.parent
133                                 else:
134                                         del path[idx]
135                                         del path[idx-1]
136
137                         node = path_to_node(base_node, path, cached_nodes)
138
139                 if not node:
140                         raise ValueError('could not find %r for %r' % (path, self))
141
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
145                         continue
146
147                 resolved_nodes.append(node)
148
149         Logs.debug('deps: gccdeps for %s returned %s', self, resolved_nodes)
150
151         bld.node_deps[self.uid()] = resolved_nodes
152         bld.raw_deps[self.uid()] = unresolved_names
153
154         try:
155                 del self.cache_sig
156         except AttributeError:
157                 pass
158
159         Task.Task.post_run(self)
160
161 def scan(self):
162         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
163                 return super(self.derived_gccdeps, self).scan()
164
165         resolved_nodes = self.generator.bld.node_deps.get(self.uid(), [])
166         unresolved_names = []
167         return (resolved_nodes, unresolved_names)
168
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
173
174         try:
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():
182                                 try:
183                                         del x.parent.children[x.name]
184                                 except KeyError:
185                                         pass
186
187         key = self.uid()
188         bld.node_deps[key] = []
189         bld.raw_deps[key] = []
190         return Utils.SIG_NIL
191
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
198
199 for k in ('asm', 'c', 'cxx'):
200         if k in Task.classes:
201                 wrap_compiled_task(k)
202
203 @before_method('process_source')
204 @feature('force_gccdeps')
205 def force_gccdeps(self):
206         self.env.ENABLE_GCCDEPS = ['asm', 'c', 'cxx']
207
208 def configure(conf):
209         # in case someone provides a --enable-gccdeps command-line option
210         if not getattr(conf.options, 'enable_gccdeps', True):
211                 return
212
213         global gccdeps_flags
214         flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
215         if conf.env.ASM_NAME in supported_compilers:
216                 try:
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:
219                         pass
220                 else:
221                         conf.env.append_value('ASFLAGS', flags)
222                         conf.env.append_unique('ENABLE_GCCDEPS', 'asm')
223
224         if conf.env.CC_NAME in supported_compilers:
225                 try:
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:
228                         pass
229                 else:
230                         conf.env.append_value('CFLAGS', flags)
231                         conf.env.append_unique('ENABLE_GCCDEPS', 'c')
232
233         if conf.env.CXX_NAME in supported_compilers:
234                 try:
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:
237                         pass
238                 else:
239                         conf.env.append_value('CXXFLAGS', flags)
240                         conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')
241
242 def options(opt):
243         raise ValueError('Do not load gccdeps options')
244