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