build: added --nonshared-binary=LIST option
[nivanova/samba-autobuild/.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     # handle any non-shared binaries
646     for t in tgt_list:
647         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
648             # replace lib deps with objlist deps
649             for l in t.final_libs:
650                 objname = l + '.objlist'
651                 t2 = bld.name_to_obj(objname, bld.env)
652                 if t2 is None:
653                     Logs.error('ERROR: subsystem %s not found' % objname)
654                     sys.exit(1)
655                 t.final_objects.add(objname)
656                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
657             t.final_libs = set()
658
659     # find any library loops
660     for t in tgt_list:
661         if t.samba_type in ['LIBRARY', 'PYTHON']:
662             for l in t.final_libs.copy():
663                 t2 = bld.name_to_obj(l, bld.env)
664                 if t.sname in t2.final_libs:
665                     # we could break this in either direction. If one of the libraries
666                     # has a version number, and will this be distributed publicly, then
667                     # we should make it the lower level library in the DAG
668                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
669                     dependency_loop(loops, t, t2.sname)
670                     t2.final_libs.remove(t.sname)
671
672
673     for loop in loops:
674         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
675
676     # we now need to make corrections for any library loops we broke up
677     # any target that depended on the target of the loop and doesn't
678     # depend on the source of the loop needs to get the loop source added
679     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
680         for t in tgt_list:
681             if t.samba_type != type: continue
682             for loop in loops:
683                 if loop in t.final_libs:
684                     diff = loops[loop].difference(t.final_libs)
685                     if t.sname in diff:
686                         diff.remove(t.sname)
687                     if t.sname in diff:
688                         diff.remove(t.sname)
689                     # make sure we don't recreate the loop again!
690                     for d in diff.copy():
691                         t2 = bld.name_to_obj(d, bld.env)
692                         if t2.samba_type == 'LIBRARY':
693                             if t.sname in t2.final_libs:
694                                 debug('deps: removing expansion %s from %s', d, t.sname)
695                                 diff.remove(d)
696                     if diff:
697                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
698                               loops[loop], diff)
699                         t.final_libs = t.final_libs.union(diff)
700
701     # remove objects that are also available in linked libs
702     count = 0
703     while reduce_objects(bld, tgt_list):
704         count += 1
705         if count > 100:
706             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
707             break
708     debug('deps: Object reduction took %u iterations', count)
709
710     # add in any syslib dependencies
711     for t in tgt_list:
712         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
713             continue
714         syslibs = set()
715         for d in t.final_objects:
716             t2 = bld.name_to_obj(d, bld.env)
717             syslibs = syslibs.union(t2.direct_syslibs)
718         # this adds the indirect syslibs as well, which may not be needed
719         # depending on the linker flags
720         for d in t.final_libs:
721             t2 = bld.name_to_obj(d, bld.env)
722             syslibs = syslibs.union(t2.direct_syslibs)
723         t.final_syslibs = syslibs
724
725
726     # find any unresolved library loops
727     lib_loop_error = False
728     for t in tgt_list:
729         if t.samba_type in ['LIBRARY', 'PYTHON']:
730             for l in t.final_libs.copy():
731                 t2 = bld.name_to_obj(l, bld.env)
732                 if t.sname in t2.final_libs:
733                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
734                     lib_loop_error = True
735     if lib_loop_error:
736         sys.exit(1)
737
738     debug('deps: removed duplicate dependencies')
739
740
741 ######################################################################
742 # this provides a way to save our dependency calculations between runs
743 savedeps_version = 3
744 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
745 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
746 savedeps_outenv  = ['INC_PATHS']
747 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
748 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
749
750 def save_samba_deps(bld, tgt_list):
751     '''save the dependency calculations between builds, to make
752        further builds faster'''
753     denv = Environment.Environment()
754
755     denv.version = savedeps_version
756     denv.savedeps_inputs = savedeps_inputs
757     denv.savedeps_outputs = savedeps_outputs
758     denv.input = {}
759     denv.output = {}
760     denv.outenv = {}
761     denv.caches = {}
762     denv.files  = {}
763
764     for f in savedeps_files:
765         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
766
767     for c in savedeps_caches:
768         denv.caches[c] = LOCAL_CACHE(bld, c)
769
770     for t in tgt_list:
771         # save all the input attributes for each target
772         tdeps = {}
773         for attr in savedeps_inputs:
774             v = getattr(t, attr, None)
775             if v is not None:
776                 tdeps[attr] = v
777         if tdeps != {}:
778             denv.input[t.sname] = tdeps
779
780         # save all the output attributes for each target
781         tdeps = {}
782         for attr in savedeps_outputs:
783             v = getattr(t, attr, None)
784             if v is not None:
785                 tdeps[attr] = v
786         if tdeps != {}:
787             denv.output[t.sname] = tdeps
788
789         tdeps = {}
790         for attr in savedeps_outenv:
791             if attr in t.env:
792                 tdeps[attr] = t.env[attr]
793         if tdeps != {}:
794             denv.outenv[t.sname] = tdeps
795
796     depsfile = os.path.join(bld.bdir, "sambadeps")
797     denv.store(depsfile)
798
799
800
801 def load_samba_deps(bld, tgt_list):
802     '''load a previous set of build dependencies if possible'''
803     depsfile = os.path.join(bld.bdir, "sambadeps")
804     denv = Environment.Environment()
805     try:
806         debug('deps: checking saved dependencies')
807         denv.load(depsfile)
808         if (denv.version != savedeps_version or
809             denv.savedeps_inputs != savedeps_inputs or
810             denv.savedeps_outputs != savedeps_outputs):
811             return False
812     except:
813         return False
814
815     # check if critical files have changed
816     for f in savedeps_files:
817         if f not in denv.files:
818             return False
819         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
820             return False
821
822     # check if caches are the same
823     for c in savedeps_caches:
824         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
825             return False
826
827     # check inputs are the same
828     for t in tgt_list:
829         tdeps = {}
830         for attr in savedeps_inputs:
831             v = getattr(t, attr, None)
832             if v is not None:
833                 tdeps[attr] = v
834         if t.sname in denv.input:
835             olddeps = denv.input[t.sname]
836         else:
837             olddeps = {}
838         if tdeps != olddeps:
839             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
840             return False
841
842     # put outputs in place
843     for t in tgt_list:
844         if not t.sname in denv.output: continue
845         tdeps = denv.output[t.sname]
846         for a in tdeps:
847             setattr(t, a, tdeps[a])
848
849     # put output env vars in place
850     for t in tgt_list:
851         if not t.sname in denv.outenv: continue
852         tdeps = denv.outenv[t.sname]
853         for a in tdeps:
854             t.env[a] = tdeps[a]
855
856     debug('deps: loaded saved dependencies')
857     return True
858
859
860
861 def check_project_rules(bld):
862     '''check the project rules - ensuring the targets are sane'''
863
864     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
865     loops = {}
866     inc_loops = {}
867
868     # build a list of task generators we are interested in
869     tgt_list = []
870     for tgt in targets:
871         type = targets[tgt]
872         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
873             continue
874         t = bld.name_to_obj(tgt, bld.env)
875         if t is None:
876             Logs.error("Target %s of type %s has no task generator" % (tgt, type))
877             sys.exit(1)
878         tgt_list.append(t)
879
880     add_samba_attributes(bld, tgt_list)
881
882     if load_samba_deps(bld, tgt_list):
883         return
884
885     Logs.info("Checking project rules ...")
886
887     debug('deps: project rules checking started')
888
889     expand_subsystem_deps(bld)
890     build_direct_deps(bld, tgt_list)
891     break_dependency_loops(bld, tgt_list)
892     calculate_final_deps(bld, tgt_list, loops)
893
894     # run the various attribute generators
895     for f in [ build_dependencies, build_includes, add_init_functions ]:
896         debug('deps: project rules checking %s', f)
897         for t in tgt_list: f(t)
898
899     debug('deps: project rules stage1 completed')
900
901     #check_orpaned_targets(bld, tgt_list)
902
903     if not check_duplicate_sources(bld, tgt_list):
904         Logs.error("Duplicate sources present - aborting")
905         sys.exit(1)
906
907     show_final_deps(bld, tgt_list)
908
909     debug('deps: project rules checking completed - %u targets checked',
910           len(tgt_list))
911
912     save_samba_deps(bld, tgt_list)
913
914     Logs.info("Project rules pass")
915
916
917 def CHECK_PROJECT_RULES(bld):
918     '''enable checking of project targets for sanity'''
919     if bld.env.added_project_rules:
920         return
921     bld.env.added_project_rules = True
922     bld.add_pre_fun(check_project_rules)
923 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
924
925