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