build:wafsamba: Use the Waf 1.8 API get_tgen_by_name instead of name_to_obj
[samba.git] / buildtools / wafsamba / samba_optimisation.py
1 # This file contains waf optimisations for Samba
2
3 # most of these optimisations are possible because of the restricted build environment
4 # that Samba has. For example, Samba doesn't attempt to cope with Win32 paths during the
5 # build, and Samba doesn't need build varients
6
7 # overall this makes some build tasks quite a bit faster
8
9 import os
10 import Build, Utils, Node
11 from TaskGen import feature, after, before
12 import preproc
13
14 @feature('cc', 'cxx')
15 @after('apply_type_vars', 'apply_lib_vars', 'apply_core')
16 def apply_incpaths(self):
17     lst = []
18
19     try:
20         kak = self.bld.kak
21     except AttributeError:
22         kak = self.bld.kak = {}
23
24     # TODO move the uselib processing out of here
25     for lib in self.to_list(self.uselib):
26         for path in self.env['CPPPATH_' + lib]:
27             if not path in lst:
28                 lst.append(path)
29     if preproc.go_absolute:
30         for path in preproc.standard_includes:
31             if not path in lst:
32                 lst.append(path)
33
34     for path in self.to_list(self.includes):
35         if not path in lst:
36             if preproc.go_absolute or path[0] != '/':  # os.path.isabs(path):
37                 lst.append(path)
38             else:
39                 self.env.prepend_value('CPPPATH', path)
40
41     for path in lst:
42         node = None
43         if path[0] == '/': # os.path.isabs(path):
44             if preproc.go_absolute:
45                 node = self.bld.root.find_dir(path)
46         elif path[0] == '#':
47             node = self.bld.srcnode
48             if len(path) > 1:
49                 try:
50                     node = kak[path]
51                 except KeyError:
52                     kak[path] = node = node.find_dir(path[1:])
53         else:
54             try:
55                 node = kak[(self.path.id, path)]
56             except KeyError:
57                 kak[(self.path.id, path)] = node = self.path.find_dir(path)
58
59         if node:
60             self.env.append_value('INC_PATHS', node)
61
62 @feature('cc')
63 @after('apply_incpaths')
64 def apply_obj_vars_cc(self):
65     """after apply_incpaths for INC_PATHS"""
66     env = self.env
67     app = env.append_unique
68     cpppath_st = env['CPPPATH_ST']
69
70     lss = env['_CCINCFLAGS']
71
72     try:
73          cac = self.bld.cac
74     except AttributeError:
75          cac = self.bld.cac = {}
76
77     # local flags come first
78     # set the user-defined includes paths
79     for i in env['INC_PATHS']:
80
81         try:
82             lss.extend(cac[i.id])
83         except KeyError:
84
85             cac[i.id] = [cpppath_st % i.bldpath(env), cpppath_st % i.srcpath(env)]
86             lss.extend(cac[i.id])
87
88     env['_CCINCFLAGS'] = lss
89     # set the library include paths
90     for i in env['CPPPATH']:
91         app('_CCINCFLAGS', cpppath_st % i)
92
93 import Node, Environment
94
95 def vari(self):
96     return "default"
97 Environment.Environment.variant = vari
98
99 def variant(self, env):
100     if not env: return 0
101     elif self.id & 3 == Node.FILE: return 0
102     else: return "default"
103 Node.Node.variant = variant
104
105
106 import TaskGen, Task
107
108 def create_task(self, name, src=None, tgt=None):
109     task = Task.TaskBase.classes[name](self.env, generator=self)
110     if src:
111         task.set_inputs(src)
112     if tgt:
113         task.set_outputs(tgt)
114     return task
115 TaskGen.task_gen.create_task = create_task
116
117 def hash_constraints(self):
118     a = self.attr
119     sum = hash((str(a('before', '')),
120             str(a('after', '')),
121             str(a('ext_in', '')),
122             str(a('ext_out', '')),
123             self.__class__.maxjobs))
124     return sum
125 Task.TaskBase.hash_constraints = hash_constraints
126
127 def hash_env_vars(self, env, vars_lst):
128     idx = str(id(env)) + str(vars_lst)
129     try:
130         return self.cache_sig_vars[idx]
131     except KeyError:
132         pass
133
134     m = Utils.md5()
135     m.update(''.join([str(env[a]) for a in vars_lst]))
136
137     ret = self.cache_sig_vars[idx] = m.digest()
138     return ret
139 Build.BuildContext.hash_env_vars = hash_env_vars
140
141
142 def store_fast(self, filename):
143     file = open(filename, 'wb')
144     data = self.get_merged_dict()
145     try:
146         Build.cPickle.dump(data, file, -1)
147     finally:
148         file.close()
149 Environment.Environment.store_fast = store_fast
150
151 def load_fast(self, filename):
152     file = open(filename, 'rb')
153     try:
154         data = Build.cPickle.load(file)
155     finally:
156         file.close()
157     self.table.update(data)
158 Environment.Environment.load_fast = load_fast
159
160 def is_this_a_static_lib(self, name):
161     try:
162         cache = self.cache_is_this_a_static_lib
163     except AttributeError:
164         cache = self.cache_is_this_a_static_lib = {}
165     try:
166         return cache[name]
167     except KeyError:
168         ret = cache[name] = 'cstaticlib' in self.bld.get_tgen_by_name(name).features
169         return ret
170 TaskGen.task_gen.is_this_a_static_lib = is_this_a_static_lib
171
172 def shared_ancestors(self):
173     try:
174         cache = self.cache_is_this_a_static_lib
175     except AttributeError:
176         cache = self.cache_is_this_a_static_lib = {}
177     try:
178         return cache[id(self)]
179     except KeyError:
180
181         ret = []
182         if 'cshlib' in self.features: # or 'cprogram' in self.features:
183             if getattr(self, 'uselib_local', None):
184                 lst = self.to_list(self.uselib_local)
185                 ret = [x for x in lst if not self.is_this_a_static_lib(x)]
186         cache[id(self)] = ret
187         return ret
188 TaskGen.task_gen.shared_ancestors = shared_ancestors
189
190 @feature('cc', 'cxx')
191 @after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
192 def apply_lib_vars(self):
193     """after apply_link because of 'link_task'
194     after default_cc because of the attribute 'uselib'"""
195
196     # after 'apply_core' in case if 'cc' if there is no link
197
198     env = self.env
199     app = env.append_value
200     seen_libpaths = set([])
201
202     # OPTIMIZATION 1: skip uselib variables already added (700ms)
203     seen_uselib = set([])
204
205     # 1. the case of the libs defined in the project (visit ancestors first)
206     # the ancestors external libraries (uselib) will be prepended
207     self.uselib = self.to_list(self.uselib)
208     names = self.to_list(self.uselib_local)
209
210     seen = set([])
211     tmp = Utils.deque(names) # consume a copy of the list of names
212     while tmp:
213         lib_name = tmp.popleft()
214         # visit dependencies only once
215         if lib_name in seen:
216             continue
217
218         y = self.get_tgen_by_name(lib_name)
219         if not y:
220             raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
221         y.post()
222         seen.add(lib_name)
223
224         # OPTIMIZATION 2: pre-compute ancestors shared libraries (100ms)
225         tmp.extend(y.shared_ancestors())
226
227         # link task and flags
228         if getattr(y, 'link_task', None):
229
230             link_name = y.target[y.target.rfind('/') + 1:]
231             if 'cstaticlib' in y.features:
232                 app('STATICLIB', link_name)
233             elif 'cshlib' in y.features or 'cprogram' in y.features:
234                 # WARNING some linkers can link against programs
235                 app('LIB', link_name)
236
237             # the order
238             self.link_task.set_run_after(y.link_task)
239
240             # for the recompilation
241             dep_nodes = getattr(self.link_task, 'dep_nodes', [])
242             self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
243
244             # OPTIMIZATION 3: reduce the amount of function calls
245             # add the link path too
246             par = y.link_task.outputs[0].parent
247             if id(par) not in seen_libpaths:
248                 seen_libpaths.add(id(par))
249                 tmp_path = par.bldpath(self.env)
250                 if not tmp_path in env['LIBPATH']:
251                     env.prepend_value('LIBPATH', tmp_path)
252
253
254         # add ancestors uselib too - but only propagate those that have no staticlib
255         for v in self.to_list(y.uselib):
256             if v not in seen_uselib:
257                 seen_uselib.add(v)
258                 if not env['STATICLIB_' + v]:
259                     if not v in self.uselib:
260                         self.uselib.insert(0, v)
261
262     # 2. the case of the libs defined outside
263     for x in self.uselib:
264         for v in self.p_flag_vars:
265             val = self.env[v + '_' + x]
266             if val:
267                 self.env.append_value(v, val)
268
269 @feature('cprogram', 'cshlib', 'cstaticlib')
270 @after('apply_lib_vars')
271 @before('apply_obj_vars')
272 def samba_before_apply_obj_vars(self):
273     """before apply_obj_vars for uselib, this removes the standard pathes"""
274
275     def is_standard_libpath(env, path):
276         for _path in env.STANDARD_LIBPATH:
277             if _path == os.path.normpath(path):
278                 return True
279         return False
280
281     v = self.env
282
283     for i in v['RPATH']:
284         if is_standard_libpath(v, i):
285             v['RPATH'].remove(i)
286
287     for i in v['LIBPATH']:
288         if is_standard_libpath(v, i):
289             v['LIBPATH'].remove(i)