waf: prevent the global deps list creating depenency loops
[amitay/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 def replace_grouping_libraries(bld, tgt_list):
382     '''replace dependencies based on grouping libraries
383
384     If a library is marked as a grouping library, then any target that
385     depends on a subsystem that is part of that grouping library gets
386     that dependency replaced with a dependency on the grouping library
387     '''
388
389     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
390
391     grouping = {}
392
393     # find our list of grouping libraries, mapped from the subsystems they depend on
394     for t in tgt_list:
395         if not getattr(t, 'grouping_library', False):
396             continue
397         for dep in t.samba_deps_extended:
398             if targets[dep] == 'SUBSYSTEM':
399                 grouping[dep] = t.sname
400
401     # now replace any dependencies on elements of grouping libraries
402     for t in tgt_list:
403         for i in range(len(t.samba_deps_extended)):
404             dep = t.samba_deps_extended[i]
405             if dep in grouping:
406                 if t.sname != grouping[dep]:
407                     debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
408                     t.samba_deps_extended[i] = grouping[dep]
409
410
411
412 def build_direct_deps(bld, tgt_list):
413     '''build the direct_objects and direct_libs sets for each target'''
414
415     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
416     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
417
418     global_deps = bld.env.GLOBAL_DEPENDENCIES
419     global_deps_exclude = set()
420     for dep in global_deps:
421         t = bld.name_to_obj(dep, bld.env)
422         for d in t.samba_deps:
423             # prevent loops from the global dependencies list
424             global_deps_exclude.add(d)
425             global_deps_exclude.add(d + '.objlist')
426
427     for t in tgt_list:
428         t.direct_objects = set()
429         t.direct_libs = set()
430         t.direct_syslibs = set()
431         deps = t.samba_deps_extended[:]
432         if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
433             deps.extend(global_deps)
434         for d in deps:
435             d = EXPAND_ALIAS(bld, d)
436             if d == t.sname: continue
437             if not d in targets:
438                 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
439                 sys.exit(1)
440             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
441                 continue
442             if targets[d] == 'SYSLIB':
443                 t.direct_syslibs.add(d)
444                 if d in syslib_deps:
445                     for implied in TO_LIST(syslib_deps[d]):
446                         if BUILTIN_LIBRARY(bld, implied):
447                             t.direct_objects.add(implied)
448                         elif targets[implied] == 'SYSLIB':
449                             t.direct_syslibs.add(implied)
450                         elif targets[implied] in ['LIBRARY', 'MODULE']:
451                             t.direct_libs.add(implied)
452                         else:
453                             Logs.error('Implied dependency %s in %s is of type %s' % (
454                                 implied, t.sname, targets[implied]))
455                             sys.exit(1)
456                 continue
457             t2 = bld.name_to_obj(d, bld.env)
458             if t2 is None:
459                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
460                 sys.exit(1)
461             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
462                 t.direct_libs.add(d)
463             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
464                 t.direct_objects.add(d)
465     debug('deps: built direct dependencies')
466
467
468 def dependency_loop(loops, t, target):
469     '''add a dependency loop to the loops dictionary'''
470     if t.sname == target:
471         return
472     if not target in loops:
473         loops[target] = set()
474     if not t.sname in loops[target]:
475         loops[target].add(t.sname)
476
477
478 def indirect_libs(bld, t, chain, loops):
479     '''recursively calculate the indirect library dependencies for a target
480
481     An indirect library is a library that results from a dependency on
482     a subsystem
483     '''
484
485     ret = getattr(t, 'indirect_libs', None)
486     if ret is not None:
487         return ret
488
489     ret = set()
490     for obj in t.direct_objects:
491         if obj in chain:
492             dependency_loop(loops, t, obj)
493             continue
494         chain.add(obj)
495         t2 = bld.name_to_obj(obj, bld.env)
496         r2 = indirect_libs(bld, t2, chain, loops)
497         chain.remove(obj)
498         ret = ret.union(t2.direct_libs)
499         ret = ret.union(r2)
500
501     for obj in indirect_objects(bld, t, set(), loops):
502         if obj in chain:
503             dependency_loop(loops, t, obj)
504             continue
505         chain.add(obj)
506         t2 = bld.name_to_obj(obj, bld.env)
507         r2 = indirect_libs(bld, t2, chain, loops)
508         chain.remove(obj)
509         ret = ret.union(t2.direct_libs)
510         ret = ret.union(r2)
511
512     t.indirect_libs = ret
513
514     return ret
515
516
517 def indirect_objects(bld, t, chain, loops):
518     '''recursively calculate the indirect object dependencies for a target
519
520     indirect objects are the set of objects from expanding the
521     subsystem dependencies
522     '''
523
524     ret = getattr(t, 'indirect_objects', None)
525     if ret is not None: return ret
526
527     ret = set()
528     for lib in t.direct_objects:
529         if lib in chain:
530             dependency_loop(loops, t, lib)
531             continue
532         chain.add(lib)
533         t2 = bld.name_to_obj(lib, bld.env)
534         r2 = indirect_objects(bld, t2, chain, loops)
535         chain.remove(lib)
536         ret = ret.union(t2.direct_objects)
537         ret = ret.union(r2)
538
539     t.indirect_objects = ret
540     return ret
541
542
543 def extended_objects(bld, t, chain):
544     '''recursively calculate the extended object dependencies for a target
545
546     extended objects are the union of:
547        - direct objects
548        - indirect objects
549        - direct and indirect objects of all direct and indirect libraries
550     '''
551
552     ret = getattr(t, 'extended_objects', None)
553     if ret is not None: return ret
554
555     ret = set()
556     ret = ret.union(t.final_objects)
557
558     for lib in t.final_libs:
559         if lib in chain:
560             continue
561         t2 = bld.name_to_obj(lib, bld.env)
562         chain.add(lib)
563         r2 = extended_objects(bld, t2, chain)
564         chain.remove(lib)
565         ret = ret.union(t2.final_objects)
566         ret = ret.union(r2)
567
568     t.extended_objects = ret
569     return ret
570
571
572 def includes_objects(bld, t, chain, inc_loops):
573     '''recursively calculate the includes object dependencies for a target
574
575     includes dependencies come from either library or object dependencies
576     '''
577     ret = getattr(t, 'includes_objects', None)
578     if ret is not None:
579         return ret
580
581     ret = t.direct_objects.copy()
582     ret = ret.union(t.direct_libs)
583
584     for obj in t.direct_objects:
585         if obj in chain:
586             dependency_loop(inc_loops, t, obj)
587             continue
588         chain.add(obj)
589         t2 = bld.name_to_obj(obj, bld.env)
590         r2 = includes_objects(bld, t2, chain, inc_loops)
591         chain.remove(obj)
592         ret = ret.union(t2.direct_objects)
593         ret = ret.union(r2)
594
595     for lib in t.direct_libs:
596         if lib in chain:
597             dependency_loop(inc_loops, t, lib)
598             continue
599         chain.add(lib)
600         t2 = bld.name_to_obj(lib, bld.env)
601         if t2 is None:
602             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
603             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
604                 lib, targets[lib], t.sname))
605             sys.exit(1)
606         r2 = includes_objects(bld, t2, chain, inc_loops)
607         chain.remove(lib)
608         ret = ret.union(t2.direct_objects)
609         ret = ret.union(r2)
610
611     t.includes_objects = ret
612     return ret
613
614
615 def break_dependency_loops(bld, tgt_list):
616     '''find and break dependency loops'''
617     loops = {}
618     inc_loops = {}
619
620     # build up the list of loops
621     for t in tgt_list:
622         indirect_objects(bld, t, set(), loops)
623         indirect_libs(bld, t, set(), loops)
624         includes_objects(bld, t, set(), inc_loops)
625
626     # break the loops
627     for t in tgt_list:
628         if t.sname in loops:
629             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
630                 objs = getattr(t, attr, set())
631                 setattr(t, attr, objs.difference(loops[t.sname]))
632
633     for loop in loops:
634         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
635
636     for loop in inc_loops:
637         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
638
639     # expand the loops mapping by one level
640     for loop in loops.copy():
641         for tgt in loops[loop]:
642             if tgt in loops:
643                 loops[loop] = loops[loop].union(loops[tgt])
644
645     for loop in inc_loops.copy():
646         for tgt in inc_loops[loop]:
647             if tgt in inc_loops:
648                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
649
650
651     # expand indirect subsystem and library loops
652     for loop in loops.copy():
653         t = bld.name_to_obj(loop, bld.env)
654         if t.samba_type in ['SUBSYSTEM']:
655             loops[loop] = loops[loop].union(t.indirect_objects)
656             loops[loop] = loops[loop].union(t.direct_objects)
657         if t.samba_type in ['LIBRARY','PYTHON']:
658             loops[loop] = loops[loop].union(t.indirect_libs)
659             loops[loop] = loops[loop].union(t.direct_libs)
660         if loop in loops[loop]:
661             loops[loop].remove(loop)
662
663     # expand indirect includes loops
664     for loop in inc_loops.copy():
665         t = bld.name_to_obj(loop, bld.env)
666         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
667         if loop in inc_loops[loop]:
668             inc_loops[loop].remove(loop)
669
670     # add in the replacement dependencies
671     for t in tgt_list:
672         for loop in loops:
673             for attr in ['indirect_objects', 'indirect_libs']:
674                 objs = getattr(t, attr, set())
675                 if loop in objs:
676                     diff = loops[loop].difference(objs)
677                     if t.sname in diff:
678                         diff.remove(t.sname)
679                     if diff:
680                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
681                         objs = objs.union(diff)
682                 setattr(t, attr, objs)
683
684         for loop in inc_loops:
685             objs = getattr(t, 'includes_objects', set())
686             if loop in objs:
687                 diff = inc_loops[loop].difference(objs)
688                 if t.sname in diff:
689                     diff.remove(t.sname)
690                 if diff:
691                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
692                     objs = objs.union(diff)
693             setattr(t, 'includes_objects', objs)
694
695
696 def reduce_objects(bld, tgt_list):
697     '''reduce objects by looking for indirect object dependencies'''
698     rely_on = {}
699
700     for t in tgt_list:
701         t.extended_objects = None
702
703     changed = False
704
705     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
706         for t in tgt_list:
707             if t.samba_type != type: continue
708             # if we will indirectly link to a target then we don't need it
709             new = t.final_objects.copy()
710             for l in t.final_libs:
711                 t2 = bld.name_to_obj(l, bld.env)
712                 t2_obj = extended_objects(bld, t2, set())
713                 dup = new.intersection(t2_obj)
714                 if t.sname in rely_on:
715                     dup = dup.difference(rely_on[t.sname])
716                 if dup:
717                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
718                           t.sname, t.samba_type, dup, t2.samba_type, l)
719                     new = new.difference(dup)
720                     changed = True
721                     if not l in rely_on:
722                         rely_on[l] = set()
723                     rely_on[l] = rely_on[l].union(dup)
724             t.final_objects = new
725
726     if not changed:
727         return False
728
729     # add back in any objects that were relied upon by the reduction rules
730     for r in rely_on:
731         t = bld.name_to_obj(r, bld.env)
732         t.final_objects = t.final_objects.union(rely_on[r])
733
734     return True
735
736
737 def calculate_final_deps(bld, tgt_list, loops):
738     '''calculate the final library and object dependencies'''
739     for t in tgt_list:
740         # start with the maximum possible list
741         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
742         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
743
744     for t in tgt_list:
745         # don't depend on ourselves
746         if t.sname in t.final_libs:
747             t.final_libs.remove(t.sname)
748         if t.sname in t.final_objects:
749             t.final_objects.remove(t.sname)
750
751     # handle any non-shared binaries
752     for t in tgt_list:
753         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
754             # replace lib deps with objlist deps
755             for l in t.final_libs:
756                 objname = l + '.objlist'
757                 t2 = bld.name_to_obj(objname, bld.env)
758                 if t2 is None:
759                     Logs.error('ERROR: subsystem %s not found' % objname)
760                     sys.exit(1)
761                 t.final_objects.add(objname)
762                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
763             t.final_libs = set()
764
765     # find any library loops
766     for t in tgt_list:
767         if t.samba_type in ['LIBRARY', 'PYTHON']:
768             for l in t.final_libs.copy():
769                 t2 = bld.name_to_obj(l, bld.env)
770                 if t.sname in t2.final_libs:
771                     if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
772                         # we could break this in either direction. If one of the libraries
773                         # has a version number, and will this be distributed publicly, then
774                         # we should make it the lower level library in the DAG
775                         Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
776                         dependency_loop(loops, t, t2.sname)
777                         t2.final_libs.remove(t.sname)
778                     else:
779                         Logs.error('ERROR: circular library dependency between %s and %s'
780                             % (t.sname, t2.sname))
781                         sys.exit(1)
782
783     for loop in loops:
784         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
785
786     # we now need to make corrections for any library loops we broke up
787     # any target that depended on the target of the loop and doesn't
788     # depend on the source of the loop needs to get the loop source added
789     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
790         for t in tgt_list:
791             if t.samba_type != type: continue
792             for loop in loops:
793                 if loop in t.final_libs:
794                     diff = loops[loop].difference(t.final_libs)
795                     if t.sname in diff:
796                         diff.remove(t.sname)
797                     if t.sname in diff:
798                         diff.remove(t.sname)
799                     # make sure we don't recreate the loop again!
800                     for d in diff.copy():
801                         t2 = bld.name_to_obj(d, bld.env)
802                         if t2.samba_type == 'LIBRARY':
803                             if t.sname in t2.final_libs:
804                                 debug('deps: removing expansion %s from %s', d, t.sname)
805                                 diff.remove(d)
806                     if diff:
807                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
808                               loops[loop], diff)
809                         t.final_libs = t.final_libs.union(diff)
810
811     # remove objects that are also available in linked libs
812     count = 0
813     while reduce_objects(bld, tgt_list):
814         count += 1
815         if count > 100:
816             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
817             break
818     debug('deps: Object reduction took %u iterations', count)
819
820     # add in any syslib dependencies
821     for t in tgt_list:
822         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
823             continue
824         syslibs = set()
825         for d in t.final_objects:
826             t2 = bld.name_to_obj(d, bld.env)
827             syslibs = syslibs.union(t2.direct_syslibs)
828         # this adds the indirect syslibs as well, which may not be needed
829         # depending on the linker flags
830         for d in t.final_libs:
831             t2 = bld.name_to_obj(d, bld.env)
832             syslibs = syslibs.union(t2.direct_syslibs)
833         t.final_syslibs = syslibs
834
835
836     # find any unresolved library loops
837     lib_loop_error = False
838     for t in tgt_list:
839         if t.samba_type in ['LIBRARY', 'PYTHON']:
840             for l in t.final_libs.copy():
841                 t2 = bld.name_to_obj(l, bld.env)
842                 if t.sname in t2.final_libs:
843                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
844                     lib_loop_error = True
845     if lib_loop_error:
846         sys.exit(1)
847
848     debug('deps: removed duplicate dependencies')
849
850
851 def show_dependencies(bld, target, seen):
852     '''recursively show the dependencies of target'''
853
854     if target in seen:
855         return
856
857     t = bld.name_to_obj(target, bld.env)
858     if t is None:
859         Logs.error("ERROR: Unable to find target '%s'" % target)
860         sys.exit(1)
861
862     Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
863     Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
864     Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
865
866     seen.add(target)
867
868     for t2 in t.direct_objects:
869         show_dependencies(bld, t2, seen)
870
871
872 def show_object_duplicates(bld, tgt_list):
873     '''show a list of object files that are included in more than
874     one library or binary'''
875
876     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
877
878     used_by = {}
879
880     Logs.info("showing duplicate objects")
881
882     for t in tgt_list:
883         if not targets[t.sname] in [ 'LIBRARY' ]:
884             continue
885         for n in getattr(t, 'final_objects', set()):
886             t2 = bld.name_to_obj(n, bld.env)
887             if not n in used_by:
888                 used_by[n] = set()
889             used_by[n].add(t.sname)
890
891     for n in used_by:
892         if len(used_by[n]) > 1:
893             Logs.info("target '%s' is used by %s" % (n, used_by[n]))
894
895     Logs.info("showing indirect dependency counts (sorted by count)")
896
897     def indirect_count(t1, t2):
898         return len(t2.indirect_objects) - len(t1.indirect_objects)
899
900     sorted_list = sorted(tgt_list, cmp=indirect_count)
901     for t in sorted_list:
902         if len(t.indirect_objects) > 1:
903             Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
904
905
906 ######################################################################
907 # this provides a way to save our dependency calculations between runs
908 savedeps_version = 3
909 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source', 'grouping_library']
910 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
911 savedeps_outenv  = ['INC_PATHS']
912 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES']
913 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
914 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
915
916 def save_samba_deps(bld, tgt_list):
917     '''save the dependency calculations between builds, to make
918        further builds faster'''
919     denv = Environment.Environment()
920
921     denv.version = savedeps_version
922     denv.savedeps_inputs = savedeps_inputs
923     denv.savedeps_outputs = savedeps_outputs
924     denv.input = {}
925     denv.output = {}
926     denv.outenv = {}
927     denv.caches = {}
928     denv.envvar = {}
929     denv.files  = {}
930
931     for f in savedeps_files:
932         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
933
934     for c in savedeps_caches:
935         denv.caches[c] = LOCAL_CACHE(bld, c)
936
937     for e in savedeps_envvars:
938         denv.envvar[e] = bld.env[e]
939
940     for t in tgt_list:
941         # save all the input attributes for each target
942         tdeps = {}
943         for attr in savedeps_inputs:
944             v = getattr(t, attr, None)
945             if v is not None:
946                 tdeps[attr] = v
947         if tdeps != {}:
948             denv.input[t.sname] = tdeps
949
950         # save all the output attributes for each target
951         tdeps = {}
952         for attr in savedeps_outputs:
953             v = getattr(t, attr, None)
954             if v is not None:
955                 tdeps[attr] = v
956         if tdeps != {}:
957             denv.output[t.sname] = tdeps
958
959         tdeps = {}
960         for attr in savedeps_outenv:
961             if attr in t.env:
962                 tdeps[attr] = t.env[attr]
963         if tdeps != {}:
964             denv.outenv[t.sname] = tdeps
965
966     depsfile = os.path.join(bld.bdir, "sambadeps")
967     denv.store(depsfile)
968
969
970
971 def load_samba_deps(bld, tgt_list):
972     '''load a previous set of build dependencies if possible'''
973     depsfile = os.path.join(bld.bdir, "sambadeps")
974     denv = Environment.Environment()
975     try:
976         debug('deps: checking saved dependencies')
977         denv.load(depsfile)
978         if (denv.version != savedeps_version or
979             denv.savedeps_inputs != savedeps_inputs or
980             denv.savedeps_outputs != savedeps_outputs):
981             return False
982     except:
983         return False
984
985     # check if critical files have changed
986     for f in savedeps_files:
987         if f not in denv.files:
988             return False
989         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
990             return False
991
992     # check if caches are the same
993     for c in savedeps_caches:
994         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
995             return False
996
997     # check if caches are the same
998     for e in savedeps_envvars:
999         if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1000             return False
1001
1002     # check inputs are the same
1003     for t in tgt_list:
1004         tdeps = {}
1005         for attr in savedeps_inputs:
1006             v = getattr(t, attr, None)
1007             if v is not None:
1008                 tdeps[attr] = v
1009         if t.sname in denv.input:
1010             olddeps = denv.input[t.sname]
1011         else:
1012             olddeps = {}
1013         if tdeps != olddeps:
1014             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1015             return False
1016
1017     # put outputs in place
1018     for t in tgt_list:
1019         if not t.sname in denv.output: continue
1020         tdeps = denv.output[t.sname]
1021         for a in tdeps:
1022             setattr(t, a, tdeps[a])
1023
1024     # put output env vars in place
1025     for t in tgt_list:
1026         if not t.sname in denv.outenv: continue
1027         tdeps = denv.outenv[t.sname]
1028         for a in tdeps:
1029             t.env[a] = tdeps[a]
1030
1031     debug('deps: loaded saved dependencies')
1032     return True
1033
1034
1035
1036 def check_project_rules(bld):
1037     '''check the project rules - ensuring the targets are sane'''
1038
1039     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1040     loops = {}
1041     inc_loops = {}
1042
1043     # build a list of task generators we are interested in
1044     tgt_list = []
1045     for tgt in targets:
1046         type = targets[tgt]
1047         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
1048             continue
1049         t = bld.name_to_obj(tgt, bld.env)
1050         if t is None:
1051             Logs.error("Target %s of type %s has no task generator" % (tgt, type))
1052             sys.exit(1)
1053         tgt_list.append(t)
1054
1055     add_samba_attributes(bld, tgt_list)
1056
1057     force_project_rules = (Options.options.SHOWDEPS or
1058                            Options.options.SHOW_DUPLICATES)
1059
1060     if not force_project_rules and load_samba_deps(bld, tgt_list):
1061         return
1062
1063     bld.new_rules = True    
1064     Logs.info("Checking project rules ...")
1065
1066     debug('deps: project rules checking started')
1067
1068     expand_subsystem_deps(bld)
1069     replace_grouping_libraries(bld, tgt_list)
1070     build_direct_deps(bld, tgt_list)
1071
1072     break_dependency_loops(bld, tgt_list)
1073     calculate_final_deps(bld, tgt_list, loops)
1074
1075     if Options.options.SHOWDEPS:
1076             show_dependencies(bld, Options.options.SHOWDEPS, set())
1077
1078     if Options.options.SHOW_DUPLICATES:
1079             show_object_duplicates(bld, tgt_list)
1080
1081     # run the various attribute generators
1082     for f in [ build_dependencies, build_includes, add_init_functions ]:
1083         debug('deps: project rules checking %s', f)
1084         for t in tgt_list: f(t)
1085
1086     debug('deps: project rules stage1 completed')
1087
1088     #check_orpaned_targets(bld, tgt_list)
1089
1090     if not check_duplicate_sources(bld, tgt_list):
1091         Logs.error("Duplicate sources present - aborting")
1092         sys.exit(1)
1093
1094     if not check_group_ordering(bld, tgt_list):
1095         Logs.error("Bad group ordering - aborting")
1096         sys.exit(1)
1097
1098     show_final_deps(bld, tgt_list)
1099
1100     debug('deps: project rules checking completed - %u targets checked',
1101           len(tgt_list))
1102
1103     if not bld.is_install:
1104         save_samba_deps(bld, tgt_list)
1105
1106     Logs.info("Project rules pass")
1107
1108
1109 def CHECK_PROJECT_RULES(bld):
1110     '''enable checking of project targets for sanity'''
1111     if bld.env.added_project_rules:
1112         return
1113     bld.env.added_project_rules = True
1114     bld.add_pre_fun(check_project_rules)
1115 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
1116
1117