wafsamba: flags from enviroment are put before our own internal versions
[obnox/samba/samba-obnox.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, Task
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 @before('apply_incpaths')
64 def samba_stash_cppflags(self):
65     """Fix broken waf ordering of CPPFLAGS"""
66     self.env['SAVED_CPPFLAGS'] = self.env['CPPFLAGS']
67     self.env['CPPFLAGS'] = []
68
69 @feature('cc')
70 @after('apply_incpaths')
71 def apply_obj_vars_cc(self):
72     """after apply_incpaths for INC_PATHS"""
73     env = self.env
74     app = env.append_unique
75     cpppath_st = env['CPPPATH_ST']
76
77     lss = env['_CCINCFLAGS']
78
79     try:
80          cac = self.bld.cac
81     except AttributeError:
82          cac = self.bld.cac = {}
83
84     # local flags come first
85     # set the user-defined includes paths
86     for i in env['INC_PATHS']:
87
88         try:
89             lss.extend(cac[i.id])
90         except KeyError:
91
92             cac[i.id] = [cpppath_st % i.bldpath(env), cpppath_st % i.srcpath(env)]
93             lss.extend(cac[i.id])
94
95     env['_CCINCFLAGS'] = lss
96     # set the library include paths
97     for i in env['CPPPATH']:
98         app('_CCINCFLAGS', cpppath_st % i)
99
100     # append stashed user CPPFLAGS after our internally computed flags
101     app('_CCINCFLAGS', env['SAVED_CPPFLAGS'])
102     env['SAVED_CPPFLAGS'] = []
103
104 import Node, Environment
105
106 def vari(self):
107     return "default"
108 Environment.Environment.variant = vari
109
110 def variant(self, env):
111     if not env: return 0
112     elif self.id & 3 == Node.FILE: return 0
113     else: return "default"
114 Node.Node.variant = variant
115
116
117 import TaskGen, Task
118
119 def create_task(self, name, src=None, tgt=None):
120     task = Task.TaskBase.classes[name](self.env, generator=self)
121     if src:
122         task.set_inputs(src)
123     if tgt:
124         task.set_outputs(tgt)
125     return task
126 TaskGen.task_gen.create_task = create_task
127
128 def hash_constraints(self):
129     a = self.attr
130     sum = hash((str(a('before', '')),
131             str(a('after', '')),
132             str(a('ext_in', '')),
133             str(a('ext_out', '')),
134             self.__class__.maxjobs))
135     return sum
136 Task.TaskBase.hash_constraints = hash_constraints
137
138
139 # import cc
140 # from TaskGen import extension
141 # import Utils
142
143 # @extension(cc.EXT_CC)
144 # def c_hook(self, node):
145 #     task = self.create_task('cc', node, node.change_ext('.o'))
146 #     try:
147 #         self.compiled_tasks.append(task)
148 #     except AttributeError:
149 #         raise Utils.WafError('Have you forgotten to set the feature "cc" on %s?' % str(self))
150
151 #     bld = self.bld
152 #     try:
153 #         dc = bld.dc
154 #     except AttributeError:
155 #         dc = bld.dc = {}
156
157 #     if task.outputs[0].id in dc:
158 #         raise Utils.WafError('Samba, you are doing it wrong %r %s %s' % (task.outputs, task.generator, dc[task.outputs[0].id].generator))
159 #     else:
160 #         dc[task.outputs[0].id] = task
161
162 #     return task
163
164
165 def suncc_wrap(cls):
166     '''work around a problem with cc on solaris not handling module aliases
167     which have empty libs'''
168     if getattr(cls, 'solaris_wrap', False):
169         return
170     cls.solaris_wrap = True
171     oldrun = cls.run
172     def run(self):
173         if self.env.CC_NAME == "sun" and not self.inputs:
174             self.env = self.env.copy()
175             self.env.append_value('LINKFLAGS', '-')
176         return oldrun(self)
177     cls.run = run
178 suncc_wrap(Task.TaskBase.classes['cc_link'])
179
180
181
182 def hash_env_vars(self, env, vars_lst):
183     idx = str(id(env)) + str(vars_lst)
184     try:
185         return self.cache_sig_vars[idx]
186     except KeyError:
187         pass
188
189     m = Utils.md5()
190     m.update(''.join([str(env[a]) for a in vars_lst]))
191
192     ret = self.cache_sig_vars[idx] = m.digest()
193     return ret
194 Build.BuildContext.hash_env_vars = hash_env_vars
195
196
197 def store_fast(self, filename):
198     file = open(filename, 'wb')
199     data = self.get_merged_dict()
200     try:
201         Build.cPickle.dump(data, file, -1)
202     finally:
203         file.close()
204 Environment.Environment.store_fast = store_fast
205
206 def load_fast(self, filename):
207     file = open(filename, 'rb')
208     try:
209         data = Build.cPickle.load(file)
210     finally:
211         file.close()
212     self.table.update(data)
213 Environment.Environment.load_fast = load_fast
214
215 def is_this_a_static_lib(self, name):
216     try:
217         cache = self.cache_is_this_a_static_lib
218     except AttributeError:
219         cache = self.cache_is_this_a_static_lib = {}
220     try:
221         return cache[name]
222     except KeyError:
223         ret = cache[name] = 'cstaticlib' in self.bld.name_to_obj(name, self.env).features
224         return ret
225 TaskGen.task_gen.is_this_a_static_lib = is_this_a_static_lib
226
227 def shared_ancestors(self):
228     try:
229         cache = self.cache_is_this_a_static_lib
230     except AttributeError:
231         cache = self.cache_is_this_a_static_lib = {}
232     try:
233         return cache[id(self)]
234     except KeyError:
235
236         ret = []
237         if 'cshlib' in self.features: # or 'cprogram' in self.features:
238             if getattr(self, 'uselib_local', None):
239                 lst = self.to_list(self.uselib_local)
240                 ret = [x for x in lst if not self.is_this_a_static_lib(x)]
241         cache[id(self)] = ret
242         return ret
243 TaskGen.task_gen.shared_ancestors = shared_ancestors
244
245 @feature('cc', 'cxx')
246 @after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
247 def apply_lib_vars(self):
248     """after apply_link because of 'link_task'
249     after default_cc because of the attribute 'uselib'"""
250
251     # after 'apply_core' in case if 'cc' if there is no link
252
253     env = self.env
254     app = env.append_value
255     seen_libpaths = set([])
256
257     # OPTIMIZATION 1: skip uselib variables already added (700ms)
258     seen_uselib = set([])
259
260     # 1. the case of the libs defined in the project (visit ancestors first)
261     # the ancestors external libraries (uselib) will be prepended
262     self.uselib = self.to_list(self.uselib)
263     names = self.to_list(self.uselib_local)
264
265     seen = set([])
266     tmp = Utils.deque(names) # consume a copy of the list of names
267     while tmp:
268         lib_name = tmp.popleft()
269         # visit dependencies only once
270         if lib_name in seen:
271             continue
272
273         y = self.name_to_obj(lib_name)
274         if not y:
275             raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
276         y.post()
277         seen.add(lib_name)
278
279         # OPTIMIZATION 2: pre-compute ancestors shared libraries (100ms)
280         tmp.extend(y.shared_ancestors())
281
282         # link task and flags
283         if getattr(y, 'link_task', None):
284
285             link_name = y.target[y.target.rfind('/') + 1:]
286             if 'cstaticlib' in y.features:
287                 app('STATICLIB', link_name)
288             elif 'cshlib' in y.features or 'cprogram' in y.features:
289                 # WARNING some linkers can link against programs
290                 app('LIB', link_name)
291
292             # the order
293             self.link_task.set_run_after(y.link_task)
294
295             # for the recompilation
296             dep_nodes = getattr(self.link_task, 'dep_nodes', [])
297             self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
298
299             # OPTIMIZATION 3: reduce the amount of function calls
300             # add the link path too
301             par = y.link_task.outputs[0].parent
302             if id(par) not in seen_libpaths:
303                 seen_libpaths.add(id(par))
304                 tmp_path = par.bldpath(self.env)
305                 if not tmp_path in env['LIBPATH']:
306                     env.prepend_value('LIBPATH', tmp_path)
307
308
309         # add ancestors uselib too - but only propagate those that have no staticlib
310         for v in self.to_list(y.uselib):
311             if v not in seen_uselib:
312                 seen_uselib.add(v)
313                 if not env['STATICLIB_' + v]:
314                     if not v in self.uselib:
315                         self.uselib.insert(0, v)
316
317     # 2. the case of the libs defined outside
318     for x in self.uselib:
319         for v in self.p_flag_vars:
320             val = self.env[v + '_' + x]
321             if val:
322                 self.env.append_value(v, val)
323
324 @feature('cprogram', 'cshlib', 'cstaticlib')
325 @after('apply_lib_vars')
326 @before('apply_obj_vars')
327 def samba_before_apply_obj_vars(self):
328     """before apply_obj_vars for uselib, this removes the standard pathes"""
329
330     def is_standard_libpath(env, path):
331         for _path in env.STANDARD_LIBPATH:
332             if _path == os.path.normpath(path):
333                 return True
334         return False
335
336     v = self.env
337
338     for i in v['RPATH']:
339         if is_standard_libpath(v, i):
340             v['RPATH'].remove(i)
341
342     for i in v['LIBPATH']:
343         if is_standard_libpath(v, i):
344             v['LIBPATH'].remove(i)
345
346 @feature('cprogram', 'cshlib', 'cstaticlib')
347 @before('apply_obj_vars', 'add_extra_flags')
348 def samba_stash_linkflags(self):
349     """stash away LINKFLAGS in order to fix waf's broken ordering wrt or
350     user LDFLAGS"""
351
352     self.env.SAVE_LINKFLAGS = self.env['LINKFLAGS']
353     self.env['LINKFLAGS'] = []
354
355 @feature('cprogram', 'cshlib', 'cstaticlib')
356 @after('apply_obj_vars', 'add_extra_flags')
357 def samba_pop_linkflags(self):
358     """after apply_obj_vars append saved LDFLAGS"""
359
360     self.env.append_value('LINKFLAGS', self.env.SAVE_LINKFLAGS)
361     self.env.SAVE_LINKFLAGS = []