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