build:wafsamba: Remove unnecessary parameters to cmd_and_log
[samba.git] / third_party / waf / waflib / extras / gccdeps.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2008-2010 (ita)
8
9 """
10 Execute the tasks with gcc -MD, read the dependencies from the .d file
11 and prepare the dependency calculation for the next run.
12 This affects the cxx class, so make sure to load Qt5 after this tool.
13
14 Usage:
15         def configure(conf):
16                 conf.load('gccdeps')
17 """
18
19 import os, re, threading
20 from waflib import Task, Logs, Utils, Errors
21 from waflib.Tools import c_preproc
22 from waflib.TaskGen import before_method, feature
23
24 lock = threading.Lock()
25
26 gccdeps_flags = ['-MD']
27 if not c_preproc.go_absolute:
28         gccdeps_flags = ['-MMD']
29
30 # Third-party tools are allowed to add extra names in here with append()
31 supported_compilers = ['gcc', 'icc', 'clang']
32
33 def scan(self):
34         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
35                 return super(self.derived_gccdeps, self).scan()
36         nodes = self.generator.bld.node_deps.get(self.uid(), [])
37         names = []
38         return (nodes, names)
39
40 re_o = re.compile("\.o$")
41 re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
42
43 def remove_makefile_rule_lhs(line):
44         # Splitting on a plain colon would accidentally match inside a
45         # Windows absolute-path filename, so we must search for a colon
46         # followed by whitespace to find the divider between LHS and RHS
47         # of the Makefile rule.
48         rulesep = ': '
49
50         sep_idx = line.find(rulesep)
51         if sep_idx >= 0:
52                 return line[sep_idx + 2:]
53         else:
54                 return line
55
56 def path_to_node(base_node, path, cached_nodes):
57         # Take the base node and the path and return a node
58         # Results are cached because searching the node tree is expensive
59         # The following code is executed by threads, it is not safe, so a lock is needed...
60         if getattr(path, '__hash__'):
61                 node_lookup_key = (base_node, path)
62         else:
63                 # Not hashable, assume it is a list and join into a string
64                 node_lookup_key = (base_node, os.path.sep.join(path))
65         try:
66                 lock.acquire()
67                 node = cached_nodes[node_lookup_key]
68         except KeyError:
69                 node = base_node.find_resource(path)
70                 cached_nodes[node_lookup_key] = node
71         finally:
72                 lock.release()
73         return node
74
75 def post_run(self):
76         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
77                 return super(self.derived_gccdeps, self).post_run()
78
79         name = self.outputs[0].abspath()
80         name = re_o.sub('.d', name)
81         try:
82                 txt = Utils.readf(name)
83         except EnvironmentError:
84                 Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
85                 raise
86         #os.remove(name)
87
88         # Compilers have the choice to either output the file's dependencies
89         # as one large Makefile rule:
90         #
91         #   /path/to/file.o: /path/to/dep1.h \
92         #                    /path/to/dep2.h \
93         #                    /path/to/dep3.h \
94         #                    ...
95         #
96         # or as many individual rules:
97         #
98         #   /path/to/file.o: /path/to/dep1.h
99         #   /path/to/file.o: /path/to/dep2.h
100         #   /path/to/file.o: /path/to/dep3.h
101         #   ...
102         #
103         # So the first step is to sanitize the input by stripping out the left-
104         # hand side of all these lines. After that, whatever remains are the
105         # implicit dependencies of task.outputs[0]
106         txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
107
108         # Now join all the lines together
109         txt = txt.replace('\\\n', '')
110
111         val = txt.strip()
112         val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
113
114         nodes = []
115         bld = self.generator.bld
116
117         # Dynamically bind to the cache
118         try:
119                 cached_nodes = bld.cached_nodes
120         except AttributeError:
121                 cached_nodes = bld.cached_nodes = {}
122
123         for x in val:
124
125                 node = None
126                 if os.path.isabs(x):
127                         node = path_to_node(bld.root, x, cached_nodes)
128                 else:
129                         # TODO waf 1.9 - single cwd value
130                         path = getattr(bld, 'cwdx', bld.bldnode)
131                         # when calling find_resource, make sure the path does not contain '..'
132                         x = [k for k in Utils.split_path(x) if k and k != '.']
133                         while '..' in x:
134                                 idx = x.index('..')
135                                 if idx == 0:
136                                         x = x[1:]
137                                         path = path.parent
138                                 else:
139                                         del x[idx]
140                                         del x[idx-1]
141
142                         node = path_to_node(path, x, cached_nodes)
143
144                 if not node:
145                         raise ValueError('could not find %r for %r' % (x, self))
146                 if id(node) == id(self.inputs[0]):
147                         # ignore the source file, it is already in the dependencies
148                         # this way, successful config tests may be retrieved from the cache
149                         continue
150                 nodes.append(node)
151
152         Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
153
154         bld.node_deps[self.uid()] = nodes
155         bld.raw_deps[self.uid()] = []
156
157         try:
158                 del self.cache_sig
159         except AttributeError:
160                 pass
161
162         Task.Task.post_run(self)
163
164 def sig_implicit_deps(self):
165         if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
166                 return super(self.derived_gccdeps, self).sig_implicit_deps()
167         try:
168                 return Task.Task.sig_implicit_deps(self)
169         except Errors.WafError:
170                 return Utils.SIG_NIL
171
172 def wrap_compiled_task(classname):
173         derived_class = type(classname, (Task.classes[classname],), {})
174         derived_class.derived_gccdeps = derived_class
175         derived_class.post_run = post_run
176         derived_class.scan = scan
177         derived_class.sig_implicit_deps = sig_implicit_deps
178
179 for k in ('c', 'cxx'):
180         if k in Task.classes:
181                 wrap_compiled_task(k)
182
183 @before_method('process_source')
184 @feature('force_gccdeps')
185 def force_gccdeps(self):
186         self.env.ENABLE_GCCDEPS = ['c', 'cxx']
187
188 def configure(conf):
189         # in case someone provides a --enable-gccdeps command-line option
190         if not getattr(conf.options, 'enable_gccdeps', True):
191                 return
192
193         global gccdeps_flags
194         flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
195         if conf.env.CC_NAME in supported_compilers:
196                 try:
197                         conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
198                 except Errors.ConfigurationError:
199                         pass
200                 else:
201                         conf.env.append_value('CFLAGS', gccdeps_flags)
202                         conf.env.append_unique('ENABLE_GCCDEPS', 'c')
203
204         if conf.env.CXX_NAME in supported_compilers:
205                 try:
206                         conf.check(fragment='int main() { return 0; }', features='cxx force_gccdeps', cxxflags=flags, msg='Checking for cxx flags %r' % ''.join(flags))
207                 except Errors.ConfigurationError:
208                         pass
209                 else:
210                         conf.env.append_value('CXXFLAGS', gccdeps_flags)
211                         conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')