third_party/waf: upgrade to waf 2.0.8
[Samba.git] / third_party / waf / waflib / extras / gccdeps.py
blobd9758ab34d5a1b07c59933c6d0464dbdde12be46
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2008-2010 (ita)
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.
10 Usage::
12 def options(opt):
13 opt.load('compiler_cxx')
14 def configure(conf):
15 conf.load('compiler_cxx gccdeps')
16 """
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']
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)
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.
47 rulesep = ': '
49 sep_idx = line.find(rulesep)
50 if sep_idx >= 0:
51 return line[sep_idx + 2:]
52 else:
53 return line
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
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()
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)
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 \
91 # /path/to/dep2.h \
92 # /path/to/dep3.h \
93 # ...
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
100 # ...
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', '')
110 val = txt.strip()
111 val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
113 nodes = []
114 bld = self.generator.bld
116 # Dynamically bind to the cache
117 try:
118 cached_nodes = bld.cached_nodes
119 except AttributeError:
120 cached_nodes = bld.cached_nodes = {}
122 for x in val:
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]
141 node = path_to_node(path, x, cached_nodes)
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)
151 Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
153 bld.node_deps[self.uid()] = nodes
154 bld.raw_deps[self.uid()] = []
156 try:
157 del self.cache_sig
158 except AttributeError:
159 pass
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()
166 try:
167 return Task.Task.sig_implicit_deps(self)
168 except Errors.WafError:
169 return Utils.SIG_NIL
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']
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
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')
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')
212 def options(opt):
213 raise ValueError('Do not load gccdeps options')