third_party/waf: upgrade to waf 2.0.8
[samba.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 c_preproc
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 = ['gcc', 'icc', 'clang']
31
32 def scan(self):
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(), [])
36         names = []
37         return (nodes, names)
38
39 re_o = re.compile("\.o$")
40 re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
41
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.
47         rulesep = ': '
48
49         sep_idx = line.find(rulesep)
50         if sep_idx >= 0:
51                 return line[sep_idx + 2:]
52         else:
53                 return line
54
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)
61         else:
62                 # Not hashable, assume it is a list and join into a string
63                 node_lookup_key = (base_node, os.path.sep.join(path))
64         try:
65                 lock.acquire()
66                 node = cached_nodes[node_lookup_key]
67         except KeyError:
68                 node = base_node.find_resource(path)
69                 cached_nodes[node_lookup_key] = node
70         finally:
71                 lock.release()
72         return node
73
74 def post_run(self):
75         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
76                 return super(self.derived_gccdeps, self).post_run()
77
78         name = self.outputs[0].abspath()
79         name = re_o.sub('.d', name)
80         try:
81                 txt = Utils.readf(name)
82         except EnvironmentError:
83                 Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
84                 raise
85         #os.remove(name)
86
87         # Compilers have the choice to either output the file's dependencies
88         # as one large Makefile rule:
89         #
90         #   /path/to/file.o: /path/to/dep1.h \
91         #                    /path/to/dep2.h \
92         #                    /path/to/dep3.h \
93         #                    ...
94         #
95         # or as many individual rules:
96         #
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
100         #   ...
101         #
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()])
106
107         # Now join all the lines together
108         txt = txt.replace('\\\n', '')
109
110         val = txt.strip()
111         val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
112
113         nodes = []
114         bld = self.generator.bld
115
116         # Dynamically bind to the cache
117         try:
118                 cached_nodes = bld.cached_nodes
119         except AttributeError:
120                 cached_nodes = bld.cached_nodes = {}
121
122         for x in val:
123
124                 node = None
125                 if os.path.isabs(x):
126                         node = path_to_node(bld.root, x, cached_nodes)
127                 else:
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 != '.']
132                         while '..' in x:
133                                 idx = x.index('..')
134                                 if idx == 0:
135                                         x = x[1:]
136                                         path = path.parent
137                                 else:
138                                         del x[idx]
139                                         del x[idx-1]
140
141                         node = path_to_node(path, x, cached_nodes)
142
143                 if not node:
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
148                         continue
149                 nodes.append(node)
150
151         Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
152
153         bld.node_deps[self.uid()] = nodes
154         bld.raw_deps[self.uid()] = []
155
156         try:
157                 del self.cache_sig
158         except AttributeError:
159                 pass
160
161         Task.Task.post_run(self)
162
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()
166         try:
167                 return Task.Task.sig_implicit_deps(self)
168         except Errors.WafError:
169                 return Utils.SIG_NIL
170
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
177
178 for k in ('c', 'cxx'):
179         if k in Task.classes:
180                 wrap_compiled_task(k)
181
182 @before_method('process_source')
183 @feature('force_gccdeps')
184 def force_gccdeps(self):
185         self.env.ENABLE_GCCDEPS = ['c', 'cxx']
186
187 def configure(conf):
188         # in case someone provides a --enable-gccdeps command-line option
189         if not getattr(conf.options, 'enable_gccdeps', True):
190                 return
191
192         global gccdeps_flags
193         flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
194         if conf.env.CC_NAME in supported_compilers:
195                 try:
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:
198                         pass
199                 else:
200                         conf.env.append_value('CFLAGS', gccdeps_flags)
201                         conf.env.append_unique('ENABLE_GCCDEPS', 'c')
202
203         if conf.env.CXX_NAME in supported_compilers:
204                 try:
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:
207                         pass
208                 else:
209                         conf.env.append_value('CXXFLAGS', gccdeps_flags)
210                         conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')
211
212 def options(opt):
213         raise ValueError('Do not load gccdeps options')
214