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