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