build: added abi_type_maps for FC12 struct va_list
[samba.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, re, Environment, Logs
4 from samba_utils import *
5 from samba_autoconf import *
6 from samba_bundled import BUILTIN_LIBRARY
7
8 @conf
9 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
10     '''add a dependency for all binaries and libraries'''
11     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
12         ctx.env.GLOBAL_DEPENDENCIES = []
13     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
14
15
16 def TARGET_ALIAS(bld, target, alias):
17     '''define an alias for a target name'''
18     cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
19     if alias in cache:
20         Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
21         sys.exit(1)
22     cache[alias] = target
23 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
24
25
26 @conf
27 def SET_SYSLIB_DEPS(conf, target, deps):
28     '''setup some implied dependencies for a SYSLIB'''
29     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
30     cache[target] = deps
31
32
33 def EXPAND_ALIAS(bld, target):
34     '''expand a target name via an alias'''
35     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
36     if target in aliases:
37         return aliases[target]
38     return target
39 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
40
41
42 def expand_subsystem_deps(bld):
43     '''expand the reverse dependencies resulting from subsystem
44        attributes of modules'''
45     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
46     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
47     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
48
49     for s in subsystems:
50         if s in aliases:
51             s = aliases[s]
52         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
53         type = targets[s]
54         if type == 'DISABLED' or type == 'EMPTY':
55             continue
56
57         t = bld.name_to_obj(s, bld.env)
58         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
59         for d in subsystems[s]:
60             type = targets[d['TARGET']]
61             if type != 'DISABLED' and type != 'EMPTY':
62                 t.samba_deps_extended.append(d['TARGET'])
63                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
64                 t2.samba_includes_extended.extend(t.samba_includes_extended)
65                 t2.samba_deps_extended.extend(t.samba_deps_extended)
66         t.samba_deps_extended = unique_list(t.samba_deps_extended)
67
68
69
70 def build_dependencies(self):
71     '''This builds the dependency list for a target. It runs after all the targets are declared
72
73     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
74     the full dependency list for a target until we have all of the targets declared.
75     '''
76
77     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
78         self.uselib        = list(self.final_syslibs)
79         self.uselib_local  = list(self.final_libs)
80         self.add_objects   = list(self.final_objects)
81
82         # extra link flags from pkg_config
83         libs = self.final_syslibs.copy()
84
85         (ccflags, ldflags) = library_flags(self, list(libs))
86         new_ldflags        = getattr(self, 'ldflags', [])
87         new_ldflags.extend(ldflags)
88         self.ldflags       = new_ldflags
89
90         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
91               self.sname, self.uselib, self.uselib_local, self.add_objects)
92
93     if self.samba_type in ['SUBSYSTEM']:
94         # this is needed for the ccflags of libs that come from pkg_config
95         self.uselib = list(self.direct_syslibs)
96
97     if getattr(self, 'uselib', None):
98         up_list = []
99         for l in self.uselib:
100            up_list.append(l.upper())
101         self.uselib = up_list
102
103 def build_includes(self):
104     '''This builds the right set of includes for a target.
105
106     One tricky part of this is that the includes= attribute for a
107     target needs to use paths which are relative to that targets
108     declaration directory (which we can get at via t.path).
109
110     The way this works is the includes list gets added as
111     samba_includes in the main build task declaration. Then this
112     function runs after all of the tasks are declared, and it
113     processes the samba_includes attribute to produce a includes=
114     attribute
115     '''
116
117     if getattr(self, 'samba_includes', None) is None:
118         return
119
120     bld = self.bld
121
122     inc_deps = includes_objects(bld, self, set(), {})
123
124     includes = []
125
126     # maybe add local includes
127     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
128         includes.append('.')
129
130     includes.extend(self.samba_includes_extended)
131
132     if 'EXTRA_INCLUDES' in bld.env:
133         includes.extend(bld.env['EXTRA_INCLUDES'])
134
135     includes.append('#')
136
137     inc_set = set()
138     inc_abs = []
139
140     for d in inc_deps:
141         t = bld.name_to_obj(d, bld.env)
142         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
143         inclist = getattr(t, 'samba_includes_extended', [])
144         if getattr(t, 'local_include', True) == True:
145             inclist.append('.')
146         if inclist == []:
147             continue
148         tpath = t.samba_abspath
149         for inc in inclist:
150             npath = tpath + '/' + inc
151             if not npath in inc_set:
152                 inc_abs.append(npath)
153                 inc_set.add(npath)
154
155     mypath = self.path.abspath(bld.env)
156     for inc in inc_abs:
157         relpath = os_path_relpath(inc, mypath)
158         includes.append(relpath)
159
160     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
161         includes.append('.')
162
163     # now transform the includes list to be relative to the top directory
164     # which is represented by '#' in waf. This allows waf to cache the
165     # includes lists more efficiently
166     includes_top = []
167     for i in includes:
168         if i[0] == '#':
169             # some are already top based
170             includes_top.append(i)
171             continue
172         absinc = os.path.join(self.path.abspath(), i)
173         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
174         includes_top.append('#' + relinc)
175
176     self.includes = unique_list(includes_top)
177     debug('deps: includes for target %s: includes=%s',
178           self.sname, self.includes)
179
180
181
182
183 def add_init_functions(self):
184     '''This builds the right set of init functions'''
185
186     bld = self.bld
187
188     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
189
190     # cope with the separated object lists from BINARY and LIBRARY targets
191     sname = self.sname
192     if sname.endswith('.objlist'):
193         sname = sname[0:-8]
194
195     modules = []
196     if sname in subsystems:
197         modules.append(sname)
198
199     m = getattr(self, 'samba_modules', None)
200     if m is not None:
201         modules.extend(TO_LIST(m))
202
203     m = getattr(self, 'samba_subsystem', None)
204     if m is not None:
205         modules.append(m)
206
207     if modules == []:
208         return
209
210     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
211
212     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
213
214     cflags = getattr(self, 'samba_cflags', [])[:]
215     for m in modules:
216         bld.ASSERT(m in subsystems,
217                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
218         init_fn_list = []
219         for d in subsystems[m]:
220             if targets[d['TARGET']] != 'DISABLED':
221                 init_fn_list.append(d['INIT_FUNCTION'])
222         if init_fn_list == []:
223             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
224         else:
225             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
226     self.ccflags = cflags
227
228
229
230 def check_duplicate_sources(bld, tgt_list):
231     '''see if we are compiling the same source file into multiple
232     subsystem targets for the same library or binary'''
233
234     debug('deps: checking for duplicate sources')
235
236     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
237     ret = True
238
239     seen = set()
240
241     for t in tgt_list:
242         obj_sources = getattr(t, 'source', '')
243         tpath = os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default')
244         obj_sources = bld.SUBDIR(tpath, obj_sources)
245         t.samba_source_set = set(TO_LIST(obj_sources))
246
247     for t in tgt_list:
248         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
249             continue
250
251         sources = []
252         for obj in t.add_objects:
253             t2 = t.bld.name_to_obj(obj, bld.env)
254             source_set = getattr(t2, 'samba_source_set', set())
255             sources.append( { 'dep':obj, 'src':source_set} )
256         for s in sources:
257             for s2 in sources:
258                 if s['dep'] == s2['dep']: continue
259                 common = s['src'].intersection(s2['src'])
260                 if common.difference(seen):
261                     Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
262                                                                                       s['dep'], s2['dep'],
263                                                                                       common))
264                     seen = seen.union(common)
265                     ret = False
266     return ret
267
268
269 def check_orpaned_targets(bld, tgt_list):
270     '''check if any build targets are orphaned'''
271
272     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
273
274     debug('deps: checking for orphaned targets')
275
276     for t in tgt_list:
277         if getattr(t, 'samba_used', False) == True:
278             continue
279         type = target_dict[t.sname]
280         if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
281             if re.search('^PIDL_', t.sname) is None:
282                 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
283
284
285 def show_final_deps(bld, tgt_list):
286     '''show the final dependencies for all targets'''
287
288     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
289
290     for t in tgt_list:
291         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
292             continue
293         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
294               t.sname, t.uselib, t.uselib_local, t.add_objects)
295
296
297 def add_samba_attributes(bld, tgt_list):
298     '''ensure a target has a the required samba attributes'''
299
300     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
301
302     for t in tgt_list:
303         if t.name != '':
304             t.sname = t.name
305         else:
306             t.sname = t.target
307         t.samba_type = targets[t.sname]
308         t.samba_abspath = t.path.abspath(bld.env)
309         t.samba_deps_extended = t.samba_deps[:]
310         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
311         t.ccflags = getattr(t, 'samba_cflags', '')
312
313
314 def build_direct_deps(bld, tgt_list):
315     '''build the direct_objects and direct_libs sets for each target'''
316
317     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
318     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
319     global_deps = bld.env.GLOBAL_DEPENDENCIES
320
321     for t in tgt_list:
322         t.direct_objects = set()
323         t.direct_libs = set()
324         t.direct_syslibs = set()
325         deps = t.samba_deps_extended
326         if getattr(t, 'samba_use_global_deps', False):
327             deps.extend(global_deps)
328         for d in deps:
329             d = EXPAND_ALIAS(bld, d)
330             if d == t.sname: continue
331             if not d in targets:
332                 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
333                 sys.exit(1)
334             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
335                 continue
336             if targets[d] == 'SYSLIB':
337                 t.direct_syslibs.add(d)
338                 if d in syslib_deps:
339                     for implied in TO_LIST(syslib_deps[d]):
340                         if BUILTIN_LIBRARY(bld, implied):
341                             t.direct_objects.add(implied)
342                         elif targets[implied] == 'SYSLIB':
343                             t.direct_syslibs.add(implied)
344                         elif targets[implied] in ['LIBRARY', 'MODULE']:
345                             t.direct_libs.add(implied)
346                         else:
347                             Logs.error('Implied dependency %s in %s is of type %s' % (
348                                 implied, t.sname, targets[implied]))
349                             sys.exit(1)
350                 continue
351             t2 = bld.name_to_obj(d, bld.env)
352             if t2 is None:
353                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
354                 sys.exit(1)
355             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
356                 t.direct_libs.add(d)
357             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
358                 t.direct_objects.add(d)
359     debug('deps: built direct dependencies')
360
361
362 def dependency_loop(loops, t, target):
363     '''add a dependency loop to the loops dictionary'''
364     if t.sname == target:
365         return
366     if not target in loops:
367         loops[target] = set()
368     if not t.sname in loops[target]:
369         loops[target].add(t.sname)
370
371
372 def indirect_libs(bld, t, chain, loops):
373     '''recursively calculate the indirect library dependencies for a target
374
375     An indirect library is a library that results from a dependency on
376     a subsystem
377     '''
378
379     ret = getattr(t, 'indirect_libs', None)
380     if ret is not None:
381         return ret
382
383     ret = set()
384     for obj in t.direct_objects:
385         if obj in chain:
386             dependency_loop(loops, t, obj)
387             continue
388         chain.add(obj)
389         t2 = bld.name_to_obj(obj, bld.env)
390         r2 = indirect_libs(bld, t2, chain, loops)
391         chain.remove(obj)
392         ret = ret.union(t2.direct_libs)
393         ret = ret.union(r2)
394
395     for obj in indirect_objects(bld, t, set(), loops):
396         if obj in chain:
397             dependency_loop(loops, t, obj)
398             continue
399         chain.add(obj)
400         t2 = bld.name_to_obj(obj, bld.env)
401         r2 = indirect_libs(bld, t2, chain, loops)
402         chain.remove(obj)
403         ret = ret.union(t2.direct_libs)
404         ret = ret.union(r2)
405
406     t.indirect_libs = ret
407
408     return ret
409
410
411 def indirect_objects(bld, t, chain, loops):
412     '''recursively calculate the indirect object dependencies for a target
413
414     indirect objects are the set of objects from expanding the
415     subsystem dependencies
416     '''
417
418     ret = getattr(t, 'indirect_objects', None)
419     if ret is not None: return ret
420
421     ret = set()
422     for lib in t.direct_objects:
423         if lib in chain:
424             dependency_loop(loops, t, lib)
425             continue
426         chain.add(lib)
427         t2 = bld.name_to_obj(lib, bld.env)
428         r2 = indirect_objects(bld, t2, chain, loops)
429         chain.remove(lib)
430         ret = ret.union(t2.direct_objects)
431         ret = ret.union(r2)
432
433     t.indirect_objects = ret
434     return ret
435
436
437 def extended_objects(bld, t, chain):
438     '''recursively calculate the extended object dependencies for a target
439
440     extended objects are the union of:
441        - direct objects
442        - indirect objects
443        - direct and indirect objects of all direct and indirect libraries
444     '''
445
446     ret = getattr(t, 'extended_objects', None)
447     if ret is not None: return ret
448
449     ret = set()
450     ret = ret.union(t.final_objects)
451
452     for lib in t.final_libs:
453         if lib in chain:
454             continue
455         t2 = bld.name_to_obj(lib, bld.env)
456         chain.add(lib)
457         r2 = extended_objects(bld, t2, chain)
458         chain.remove(lib)
459         ret = ret.union(t2.final_objects)
460         ret = ret.union(r2)
461
462     t.extended_objects = ret
463     return ret
464
465
466 def includes_objects(bld, t, chain, inc_loops):
467     '''recursively calculate the includes object dependencies for a target
468
469     includes dependencies come from either library or object dependencies
470     '''
471     ret = getattr(t, 'includes_objects', None)
472     if ret is not None:
473         return ret
474
475     ret = t.direct_objects.copy()
476     ret = ret.union(t.direct_libs)
477
478     for obj in t.direct_objects:
479         if obj in chain:
480             dependency_loop(inc_loops, t, obj)
481             continue
482         chain.add(obj)
483         t2 = bld.name_to_obj(obj, bld.env)
484         r2 = includes_objects(bld, t2, chain, inc_loops)
485         chain.remove(obj)
486         ret = ret.union(t2.direct_objects)
487         ret = ret.union(r2)
488
489     for lib in t.direct_libs:
490         if lib in chain:
491             dependency_loop(inc_loops, t, lib)
492             continue
493         chain.add(lib)
494         t2 = bld.name_to_obj(lib, bld.env)
495         if t2 is None:
496             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
497             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
498                 lib, targets[lib], t.sname))
499             sys.exit(1)
500         r2 = includes_objects(bld, t2, chain, inc_loops)
501         chain.remove(lib)
502         ret = ret.union(t2.direct_objects)
503         ret = ret.union(r2)
504
505     t.includes_objects = ret
506     return ret
507
508
509 def break_dependency_loops(bld, tgt_list):
510     '''find and break dependency loops'''
511     loops = {}
512     inc_loops = {}
513
514     # build up the list of loops
515     for t in tgt_list:
516         indirect_objects(bld, t, set(), loops)
517         indirect_libs(bld, t, set(), loops)
518         includes_objects(bld, t, set(), inc_loops)
519
520     # break the loops
521     for t in tgt_list:
522         if t.sname in loops:
523             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
524                 objs = getattr(t, attr, set())
525                 setattr(t, attr, objs.difference(loops[t.sname]))
526
527     for loop in loops:
528         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
529
530     for loop in inc_loops:
531         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
532
533     # expand the loops mapping by one level
534     for loop in loops.copy():
535         for tgt in loops[loop]:
536             if tgt in loops:
537                 loops[loop] = loops[loop].union(loops[tgt])
538
539     for loop in inc_loops.copy():
540         for tgt in inc_loops[loop]:
541             if tgt in inc_loops:
542                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
543
544
545     # expand indirect subsystem and library loops
546     for loop in loops.copy():
547         t = bld.name_to_obj(loop, bld.env)
548         if t.samba_type in ['SUBSYSTEM']:
549             loops[loop] = loops[loop].union(t.indirect_objects)
550             loops[loop] = loops[loop].union(t.direct_objects)
551         if t.samba_type in ['LIBRARY','PYTHON']:
552             loops[loop] = loops[loop].union(t.indirect_libs)
553             loops[loop] = loops[loop].union(t.direct_libs)
554         if loop in loops[loop]:
555             loops[loop].remove(loop)
556
557     # expand indirect includes loops
558     for loop in inc_loops.copy():
559         t = bld.name_to_obj(loop, bld.env)
560         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
561         if loop in inc_loops[loop]:
562             inc_loops[loop].remove(loop)
563
564     # add in the replacement dependencies
565     for t in tgt_list:
566         for loop in loops:
567             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
568                 objs = getattr(t, attr, set())
569                 if loop in objs:
570                     diff = loops[loop].difference(objs)
571                     if t.sname in diff:
572                         diff.remove(t.sname)
573                     if diff:
574                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
575                         objs = objs.union(diff)
576                 setattr(t, attr, objs)
577
578         for loop in inc_loops:
579             objs = getattr(t, 'includes_objects', set())
580             if loop in objs:
581                 diff = inc_loops[loop].difference(objs)
582                 if t.sname in diff:
583                     diff.remove(t.sname)
584                 if diff:
585                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
586                     objs = objs.union(diff)
587             setattr(t, 'includes_objects', objs)
588
589
590 def reduce_objects(bld, tgt_list):
591     '''reduce objects by looking for indirect object dependencies'''
592     rely_on = {}
593
594     for t in tgt_list:
595         t.extended_objects = None
596
597     changed = False
598
599     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
600         for t in tgt_list:
601             if t.samba_type != type: continue
602             # if we will indirectly link to a target then we don't need it
603             new = t.final_objects.copy()
604             for l in t.final_libs:
605                 t2 = bld.name_to_obj(l, bld.env)
606                 t2_obj = extended_objects(bld, t2, set())
607                 dup = new.intersection(t2_obj)
608                 if t.sname in rely_on:
609                     dup = dup.difference(rely_on[t.sname])
610                 if dup:
611                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
612                           t.sname, t.samba_type, dup, t2.samba_type, l)
613                     new = new.difference(dup)
614                     changed = True
615                     if not l in rely_on:
616                         rely_on[l] = set()
617                     rely_on[l] = rely_on[l].union(dup)
618             t.final_objects = new
619
620     if not changed:
621         return False
622
623     # add back in any objects that were relied upon by the reduction rules
624     for r in rely_on:
625         t = bld.name_to_obj(r, bld.env)
626         t.final_objects = t.final_objects.union(rely_on[r])
627
628     return True
629
630
631 def calculate_final_deps(bld, tgt_list, loops):
632     '''calculate the final library and object dependencies'''
633     for t in tgt_list:
634         # start with the maximum possible list
635         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
636         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
637
638     for t in tgt_list:
639         # don't depend on ourselves
640         if t.sname in t.final_libs:
641             t.final_libs.remove(t.sname)
642         if t.sname in t.final_objects:
643             t.final_objects.remove(t.sname)
644
645
646     # find any library loops
647     for t in tgt_list:
648         if t.samba_type in ['LIBRARY', 'PYTHON']:
649             for l in t.final_libs.copy():
650                 t2 = bld.name_to_obj(l, bld.env)
651                 if t.sname in t2.final_libs:
652                     # we could break this in either direction. If one of the libraries
653                     # has a version number, and will this be distributed publicly, then
654                     # we should make it the lower level library in the DAG
655                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
656                     dependency_loop(loops, t, t2.sname)
657                     t2.final_libs.remove(t.sname)
658
659
660     for loop in loops:
661         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
662
663     # we now need to make corrections for any library loops we broke up
664     # any target that depended on the target of the loop and doesn't
665     # depend on the source of the loop needs to get the loop source added
666     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
667         for t in tgt_list:
668             if t.samba_type != type: continue
669             for loop in loops:
670                 if loop in t.final_libs:
671                     diff = loops[loop].difference(t.final_libs)
672                     if t.sname in diff:
673                         diff.remove(t.sname)
674                     if t.sname in diff:
675                         diff.remove(t.sname)
676                     # make sure we don't recreate the loop again!
677                     for d in diff.copy():
678                         t2 = bld.name_to_obj(d, bld.env)
679                         if t2.samba_type == 'LIBRARY':
680                             if t.sname in t2.final_libs:
681                                 debug('deps: removing expansion %s from %s', d, t.sname)
682                                 diff.remove(d)
683                     if diff:
684                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
685                               loops[loop], diff)
686                         t.final_libs = t.final_libs.union(diff)
687
688     # remove objects that are also available in linked libs
689     count = 0
690     while reduce_objects(bld, tgt_list):
691         count += 1
692         if count > 100:
693             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
694             break
695     debug('deps: Object reduction took %u iterations', count)
696
697     # add in any syslib dependencies
698     for t in tgt_list:
699         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
700             continue
701         syslibs = set()
702         for d in t.final_objects:
703             t2 = bld.name_to_obj(d, bld.env)
704             syslibs = syslibs.union(t2.direct_syslibs)
705         # this adds the indirect syslibs as well, which may not be needed
706         # depending on the linker flags
707         for d in t.final_libs:
708             t2 = bld.name_to_obj(d, bld.env)
709             syslibs = syslibs.union(t2.direct_syslibs)
710         t.final_syslibs = syslibs
711
712
713     # find any unresolved library loops
714     lib_loop_error = False
715     for t in tgt_list:
716         if t.samba_type in ['LIBRARY', 'PYTHON']:
717             for l in t.final_libs.copy():
718                 t2 = bld.name_to_obj(l, bld.env)
719                 if t.sname in t2.final_libs:
720                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
721                     lib_loop_error = True
722     if lib_loop_error:
723         sys.exit(1)
724
725     debug('deps: removed duplicate dependencies')
726
727
728 ######################################################################
729 # this provides a way to save our dependency calculations between runs
730 savedeps_version = 3
731 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
732 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
733 savedeps_outenv  = ['INC_PATHS']
734 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
735 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
736
737 def save_samba_deps(bld, tgt_list):
738     '''save the dependency calculations between builds, to make
739        further builds faster'''
740     denv = Environment.Environment()
741
742     denv.version = savedeps_version
743     denv.savedeps_inputs = savedeps_inputs
744     denv.savedeps_outputs = savedeps_outputs
745     denv.input = {}
746     denv.output = {}
747     denv.outenv = {}
748     denv.caches = {}
749     denv.files  = {}
750
751     for f in savedeps_files:
752         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
753
754     for c in savedeps_caches:
755         denv.caches[c] = LOCAL_CACHE(bld, c)
756
757     for t in tgt_list:
758         # save all the input attributes for each target
759         tdeps = {}
760         for attr in savedeps_inputs:
761             v = getattr(t, attr, None)
762             if v is not None:
763                 tdeps[attr] = v
764         if tdeps != {}:
765             denv.input[t.sname] = tdeps
766
767         # save all the output attributes for each target
768         tdeps = {}
769         for attr in savedeps_outputs:
770             v = getattr(t, attr, None)
771             if v is not None:
772                 tdeps[attr] = v
773         if tdeps != {}:
774             denv.output[t.sname] = tdeps
775
776         tdeps = {}
777         for attr in savedeps_outenv:
778             if attr in t.env:
779                 tdeps[attr] = t.env[attr]
780         if tdeps != {}:
781             denv.outenv[t.sname] = tdeps
782
783     depsfile = os.path.join(bld.bdir, "sambadeps")
784     denv.store(depsfile)
785
786
787
788 def load_samba_deps(bld, tgt_list):
789     '''load a previous set of build dependencies if possible'''
790     depsfile = os.path.join(bld.bdir, "sambadeps")
791     denv = Environment.Environment()
792     try:
793         debug('deps: checking saved dependencies')
794         denv.load(depsfile)
795         if (denv.version != savedeps_version or
796             denv.savedeps_inputs != savedeps_inputs or
797             denv.savedeps_outputs != savedeps_outputs):
798             return False
799     except:
800         return False
801
802     # check if critical files have changed
803     for f in savedeps_files:
804         if f not in denv.files:
805             return False
806         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
807             return False
808
809     # check if caches are the same
810     for c in savedeps_caches:
811         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
812             return False
813
814     # check inputs are the same
815     for t in tgt_list:
816         tdeps = {}
817         for attr in savedeps_inputs:
818             v = getattr(t, attr, None)
819             if v is not None:
820                 tdeps[attr] = v
821         if t.sname in denv.input:
822             olddeps = denv.input[t.sname]
823         else:
824             olddeps = {}
825         if tdeps != olddeps:
826             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
827             return False
828
829     # put outputs in place
830     for t in tgt_list:
831         if not t.sname in denv.output: continue
832         tdeps = denv.output[t.sname]
833         for a in tdeps:
834             setattr(t, a, tdeps[a])
835
836     # put output env vars in place
837     for t in tgt_list:
838         if not t.sname in denv.outenv: continue
839         tdeps = denv.outenv[t.sname]
840         for a in tdeps:
841             t.env[a] = tdeps[a]
842
843     debug('deps: loaded saved dependencies')
844     return True
845
846
847
848 def check_project_rules(bld):
849     '''check the project rules - ensuring the targets are sane'''
850
851     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
852     loops = {}
853     inc_loops = {}
854
855     # build a list of task generators we are interested in
856     tgt_list = []
857     for tgt in targets:
858         type = targets[tgt]
859         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
860             continue
861         t = bld.name_to_obj(tgt, bld.env)
862         if t is None:
863             Logs.error("Target %s of type %s has no task generator" % (tgt, type))
864             sys.exit(1)
865         tgt_list.append(t)
866
867     add_samba_attributes(bld, tgt_list)
868
869     if load_samba_deps(bld, tgt_list):
870         return
871
872     Logs.info("Checking project rules ...")
873
874     debug('deps: project rules checking started')
875
876     expand_subsystem_deps(bld)
877     build_direct_deps(bld, tgt_list)
878     break_dependency_loops(bld, tgt_list)
879     calculate_final_deps(bld, tgt_list, loops)
880
881     # run the various attribute generators
882     for f in [ build_dependencies, build_includes, add_init_functions ]:
883         debug('deps: project rules checking %s', f)
884         for t in tgt_list: f(t)
885
886     debug('deps: project rules stage1 completed')
887
888     #check_orpaned_targets(bld, tgt_list)
889
890     if not check_duplicate_sources(bld, tgt_list):
891         Logs.error("Duplicate sources present - aborting")
892         sys.exit(1)
893
894     show_final_deps(bld, tgt_list)
895
896     debug('deps: project rules checking completed - %u targets checked',
897           len(tgt_list))
898
899     save_samba_deps(bld, tgt_list)
900
901     Logs.info("Project rules pass")
902
903
904 def CHECK_PROJECT_RULES(bld):
905     '''enable checking of project targets for sanity'''
906     if bld.env.added_project_rules:
907         return
908     bld.env.added_project_rules = True
909     bld.add_pre_fun(check_project_rules)
910 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
911
912