build: optimise and re-enable check_duplicate_sources
[samba.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, re, Environment
4 from samba_utils import *
5 from samba_autoconf import *
6
7 @conf
8 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
9     '''add a dependency for all binaries and libraries'''
10     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
11         ctx.env.GLOBAL_DEPENDENCIES = []
12     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
13
14
15 def TARGET_ALIAS(bld, target, alias):
16     '''define an alias for a target name'''
17     cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
18     if alias in cache:
19         print("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
20         raise
21     cache[alias] = target
22 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
23
24
25 def EXPAND_ALIAS(bld, target):
26     '''expand a target name via an alias'''
27     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
28     if target in aliases:
29         return aliases[target]
30     return target
31 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
32
33
34 def expand_subsystem_deps(bld):
35     '''expand the reverse dependencies resulting from subsystem
36        attributes of modules'''
37     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
38     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
39     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
40
41     for s in subsystems:
42         if s in aliases:
43             s = aliases[s]
44         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
45         type = targets[s]
46         if type == 'DISABLED' or type == 'EMPTY':
47             continue
48
49         t = bld.name_to_obj(s, bld.env)
50         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
51         for d in subsystems[s]:
52             type = targets[d['TARGET']]
53             if type != 'DISABLED' and type != 'EMPTY':
54                 t.samba_deps_extended.append(d['TARGET'])
55                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
56                 t2.samba_includes_extended.extend(t.samba_includes_extended)
57                 t2.samba_deps_extended.extend(t.samba_deps_extended)
58         t.samba_deps_extended = unique_list(t.samba_deps_extended)
59
60
61
62 def build_dependencies(self):
63     '''This builds the dependency list for a target. It runs after all the targets are declared
64
65     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
66     the full dependency list for a target until we have all of the targets declared.
67     '''
68
69     # we only should add extra library and object deps on libraries and binaries
70     if not self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
71         return
72
73     # we need to link against:
74
75     #  1) any direct system libs
76     #  2) any indirect system libs that come from subsystem dependencies
77     #  3) any direct local libs
78     #  4) any indirect local libs that come from subsystem dependencies
79     #  5) any direct objects
80     #  6) any indirect objects that come from subsystem dependencies
81
82     self.uselib        = list(self.final_syslibs)
83     self.uselib_local  = list(self.final_libs)
84     self.add_objects   = list(self.final_objects)
85
86     debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
87           self.sname, self.uselib, self.uselib_local, self.add_objects)
88
89
90
91 def build_includes(self):
92     '''This builds the right set of includes for a target.
93
94     One tricky part of this is that the includes= attribute for a
95     target needs to use paths which are relative to that targets
96     declaration directory (which we can get at via t.path).
97
98     The way this works is the includes list gets added as
99     samba_includes in the main build task declaration. Then this
100     function runs after all of the tasks are declared, and it
101     processes the samba_includes attribute to produce a includes=
102     attribute
103     '''
104
105     if getattr(self, 'samba_includes', None) is None:
106         return
107
108     bld = self.bld
109
110     inc_deps = self.includes_objects
111
112     includes = []
113
114     # maybe add local includes
115     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
116         includes.append('.')
117
118     includes.extend(self.samba_includes_extended)
119
120     if 'EXTRA_INCLUDES' in bld.env:
121         includes.extend(bld.env['EXTRA_INCLUDES'])
122
123     includes.append('#')
124
125     inc_set = set()
126     inc_abs = []
127
128     for d in inc_deps:
129         t = bld.name_to_obj(d, bld.env)
130         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
131         inclist = getattr(t, 'samba_includes_extended', [])
132         if getattr(t, 'local_include', True) == True:
133             inclist.append('.')
134         if inclist == []:
135             continue
136         tpath = t.samba_abspath
137         for inc in inclist:
138             npath = tpath + '/' + inc
139             if not npath in inc_set:
140                 inc_abs.append(npath)
141                 inc_set.add(npath)
142
143     mypath = self.path.abspath(bld.env)
144     for inc in inc_abs:
145         relpath = os_path_relpath(inc, mypath)
146         includes.append(relpath)
147
148     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
149         includes.append('.')
150
151     # now transform the includes list to be relative to the top directory
152     # which is represented by '#' in waf. This allows waf to cache the
153     # includes lists more efficiently
154     includes_top = []
155     for i in includes:
156         if i[0] == '#':
157             # some are already top based
158             includes_top.append(i)
159             continue
160         absinc = os.path.join(self.path.abspath(), i)
161         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
162         includes_top.append('#' + relinc)
163
164     self.includes = unique_list(includes_top)
165     debug('deps: includes for target %s: includes=%s',
166           self.sname, self.includes)
167
168
169
170
171 def add_init_functions(self):
172     '''This builds the right set of init functions'''
173
174     bld = self.bld
175
176     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
177
178     # cope with the separated object lists from BINARY and LIBRARY targets
179     sname = self.sname
180     if sname.endswith('.objlist'):
181         sname = sname[0:-8]
182
183     modules = []
184     if sname in subsystems:
185         modules.append(sname)
186
187     m = getattr(self, 'samba_modules', None)
188     if m is not None:
189         modules.extend(TO_LIST(m))
190
191     m = getattr(self, 'samba_subsystem', None)
192     if m is not None:
193         modules.append(m)
194
195     if modules == []:
196         return
197
198     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
199
200     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
201
202     cflags = getattr(self, 'samba_cflags', [])[:]
203     for m in modules:
204         bld.ASSERT(m in subsystems,
205                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
206         init_fn_list = []
207         for d in subsystems[m]:
208             if targets[d['TARGET']] != 'DISABLED':
209                 init_fn_list.append(d['INIT_FUNCTION'])
210         if init_fn_list == []:
211             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
212         else:
213             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
214     self.ccflags = cflags
215
216
217
218 def check_duplicate_sources(bld, tgt_list):
219     '''see if we are compiling the same source file into multiple
220     subsystem targets for the same library or binary'''
221
222     debug('deps: checking for duplicate sources')
223
224     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
225     ret = True
226
227     seen = set()
228
229     '''
230     # this was useful for finding problems with the autogenerated rules
231     for t in tgt_list:
232         base_list = set()
233         sources = TO_LIST(getattr(t, 'source', ''))
234         for s in sources:
235             bname = os.path.basename(s)
236             if bname in base_list:
237                 print "Suspicious duplicate name %s in %s" % (bname, t.sname)
238                 continue
239             base_list.add(bname)
240     '''
241
242
243     for t in tgt_list:
244         obj_sources = getattr(t, 'source', '')
245         tpath = os_path_relpath(t.path.abspath(bld.env), t.env['BUILD_DIRECTORY'] + '/default')
246         obj_sources = bld.SUBDIR(tpath, obj_sources)
247         t.samba_source_set = set(TO_LIST(obj_sources))
248
249     for t in tgt_list:
250         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
251             continue
252
253         sources = []
254         for obj in t.add_objects:
255             t2 = t.bld.name_to_obj(obj, bld.env)
256             source_set = getattr(t2, 'samba_source_set', set())
257             sources.append( { 'dep':obj, 'src':source_set} )
258         for s in sources:
259             for s2 in sources:
260                 if s['dep'] == s2['dep']: continue
261                 common = s['src'].intersection(s2['src'])
262                 if common.difference(seen):
263                     print("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
264                                                                                       s['dep'], s2['dep'],
265                                                                                       common))
266                     seen = seen.union(common)
267                     ret = False
268     return ret
269
270
271 def check_orpaned_targets(bld, tgt_list):
272     '''check if any build targets are orphaned'''
273
274     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
275
276     debug('deps: checking for orphaned targets')
277
278     for t in tgt_list:
279         if getattr(t, 'samba_used', False) == True:
280             continue
281         type = target_dict[t.sname]
282         if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
283             if re.search('^PIDL_', t.sname) is None:
284                 print "Target %s of type %s is unused by any other target" % (t.sname, type)
285
286
287 def show_final_deps(bld, tgt_list):
288     '''show the final dependencies for all targets'''
289
290     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
291
292     for t in tgt_list:
293         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
294             continue
295         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
296               t.sname, t.uselib, t.uselib_local, t.add_objects)
297
298
299 def add_samba_attributes(bld, tgt_list):
300     '''ensure a target has a the required samba attributes'''
301
302     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
303
304     for t in tgt_list:
305         if t.name != '':
306             t.sname = t.name
307         else:
308             t.sname = t.target
309         t.samba_type = targets[t.sname]
310         t.samba_abspath = t.path.abspath(bld.env)
311         t.samba_deps_extended = t.samba_deps[:]
312         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
313         t.ccflags = getattr(t, 'samba_cflags', '')
314
315 def build_direct_deps(bld, tgt_list):
316     '''build the direct_objects and direct_libs sets for each target'''
317
318     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
319     global_deps = bld.env.GLOBAL_DEPENDENCIES
320
321     for t in tgt_list:
322         t.direct_objects = set()
323         t.direct_libs = set()
324         t.direct_syslibs = set()
325         deps = t.samba_deps_extended
326         deps.extend(global_deps)
327         for d in deps:
328             d = EXPAND_ALIAS(bld, d)
329             if not d in targets:
330                 print "Unknown dependency %s in %s" % (d, t.sname)
331                 raise
332             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
333                 continue
334             if targets[d] == 'SYSLIB':
335                 t.direct_syslibs.add(d)
336                 continue
337             t2 = bld.name_to_obj(d, bld.env)
338             if t2 is None:
339                 print "no task %s type %s" % (d, targets[d])
340             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
341                 t.direct_libs.add(d)
342             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
343                 t.direct_objects.add(d)
344     debug('deps: built direct dependencies')
345
346
347
348 def indirect_libs(bld, t, chain):
349     '''recursively calculate the indirect library dependencies for a target
350
351     An indirect library is a library that results from a dependency on
352     a subsystem
353     '''
354
355     ret = getattr(t, 'indirect_libs', None)
356     if ret is not None:
357         return ret
358
359     ret = set()
360     for obj in t.direct_objects:
361         if obj in chain:
362             continue
363         chain.add(obj)
364         t2 = bld.name_to_obj(obj, bld.env)
365         r2 = indirect_libs(bld, t2, chain)
366         chain.remove(obj)
367         ret = ret.union(t2.direct_libs)
368         ret = ret.union(r2)
369
370     for obj in t.indirect_objects:
371         if obj in chain:
372             continue
373         chain.add(obj)
374         t2 = bld.name_to_obj(obj, bld.env)
375         r2 = indirect_libs(bld, t2, chain)
376         chain.remove(obj)
377         ret = ret.union(t2.direct_libs)
378         ret = ret.union(r2)
379
380     t.indirect_libs = ret
381
382     return ret
383
384
385 def indirect_syslibs(bld, t, chain):
386     '''recursively calculate the indirect system library dependencies for a target
387
388     An indirect syslib results from a subsystem dependency
389     '''
390
391     ret = getattr(t, 'indirect_syslibs', None)
392     if ret is not None:
393         return ret
394     ret = set()
395     for obj in t.direct_objects:
396         if obj in chain:
397             continue
398         chain.add(obj)
399         t2 = bld.name_to_obj(obj, bld.env)
400         r2 = indirect_syslibs(bld, t2, chain)
401         chain.remove(obj)
402         ret = ret.union(t2.direct_syslibs)
403         ret = ret.union(r2)
404
405     t.indirect_syslibs = ret
406     return ret
407
408
409 def indirect_objects(bld, t, chain):
410     '''recursively calculate the indirect object dependencies for a target
411
412     indirect objects are the set of objects from expanding the
413     subsystem dependencies
414     '''
415
416     ret = getattr(t, 'indirect_objects', None)
417     if ret is not None: return ret
418
419     ret = set()
420     for lib in t.direct_objects:
421         if lib in chain:
422             continue
423         chain.add(lib)
424         t2 = bld.name_to_obj(lib, bld.env)
425         r2 = indirect_objects(bld, t2, chain)
426         chain.remove(lib)
427         ret = ret.union(t2.direct_objects)
428         ret = ret.union(r2)
429
430     t.indirect_objects = ret
431     return ret
432
433
434 def expanded_targets(bld, t, chain):
435     '''recursively calculate the expanded targets for a target
436
437     expanded objects are the set of objects, libraries and syslibs
438     from expanding the subsystem dependencies, library dependencies
439     and syslib dependencies
440     '''
441
442     ret = getattr(t, 'expanded_targets', None)
443     if ret is not None: return ret
444
445     ret = t.direct_objects.copy()
446     ret = ret.union(t.direct_libs)
447     ret = ret.union(t.direct_syslibs)
448
449     direct = ret.copy()
450
451     for d in direct:
452         if d in chain: continue
453         chain.add(d)
454         t2 = bld.name_to_obj(d, bld.env)
455         if t2 is None: continue
456         r2 = expanded_targets(bld, t2, chain)
457         chain.remove(d)
458         ret = ret.union(r2)
459
460     if t.sname in ret:
461         ret.remove(t.sname)
462
463     t.expanded_targets = ret
464     return ret
465
466
467 def expanded_targets2(bld, t, chain):
468     '''recursively calculate the expanded targets for a target
469
470     expanded objects are the set of objects from expanding the
471     subsystem dependencies and library dependencies
472     '''
473
474     ret = getattr(t, 'expanded_targets2', None)
475     if ret is not None: return ret
476
477     ret = t.final_objects.copy()
478
479     for attr in [ 'final_objects', 'final_libs' ]:
480         f = getattr(t, attr, set())
481         for d in f.copy():
482             if d in chain:
483                 continue
484             chain.add(d)
485             t2 = bld.name_to_obj(d, bld.env)
486             if t2 is None: continue
487             r2 = expanded_targets2(bld, t2, chain)
488             chain.remove(d)
489             ret = ret.union(r2)
490
491     if t.sname in ret:
492         ret.remove(t.sname)
493
494     t.expanded_targets2 = ret
495     return ret
496
497
498 def includes_objects(bld, t, chain):
499     '''recursively calculate the includes object dependencies for a target
500
501     includes dependencies come from either library or object dependencies
502     '''
503     ret = getattr(t, 'includes_objects', None)
504     if ret is not None:
505         return ret
506
507     ret = t.direct_objects.copy()
508     ret = ret.union(t.direct_libs)
509
510     for obj in t.direct_objects:
511         if obj in chain:
512             continue
513         chain.add(obj)
514         t2 = bld.name_to_obj(obj, bld.env)
515         r2 = includes_objects(bld, t2, chain)
516         chain.remove(obj)
517         ret = ret.union(t2.direct_objects)
518         ret = ret.union(r2)
519
520     for lib in t.direct_libs:
521         if lib in chain:
522             continue
523         chain.add(lib)
524         t2 = bld.name_to_obj(lib, bld.env)
525         r2 = includes_objects(bld, t2, chain)
526         chain.remove(lib)
527         ret = ret.union(t2.direct_objects)
528         ret = ret.union(r2)
529
530     t.includes_objects = ret
531     return ret
532
533
534 def build_indirect_deps(bld, tgt_list):
535     '''build the indirect_objects and indirect_libs sets for each target'''
536     for t in tgt_list:
537         indirect_objects(bld, t, set())
538         indirect_libs(bld, t, set())
539         indirect_syslibs(bld, t, set())
540         includes_objects(bld, t, set())
541         expanded_targets(bld, t, set())
542     debug('deps: built indirect dependencies')
543
544
545 def re_expand2(bld, tgt_list):
546     for t in tgt_list:
547         t.expanded_targets2 = None
548     for type in ['BINARY','LIBRARY','PYTHON']:
549         for t in tgt_list:
550             if t.samba_type == type:
551                 expanded_targets2(bld, t, set())
552     for t in tgt_list:
553         expanded_targets2(bld, t, set())
554
555
556 def calculate_final_deps(bld, tgt_list):
557     '''calculate the final library and object dependencies'''
558     for t in tgt_list:
559         # start with the maximum possible list
560         t.final_syslibs = t.direct_syslibs.union(t.indirect_syslibs)
561         t.final_libs    = t.direct_libs.union(t.indirect_libs)
562         t.final_objects = t.direct_objects.union(t.indirect_objects)
563
564     for t in tgt_list:
565         # don't depend on ourselves
566         if t.sname in t.final_libs:
567             t.final_libs.remove(t.sname)
568         if t.sname in t.final_objects:
569             t.final_objects.remove(t.sname)
570
571     re_expand2(bld, tgt_list)
572
573     loops = {}
574
575     # find any library loops
576     for t in tgt_list:
577         if t.samba_type in ['LIBRARY', 'PYTHON']:
578             for l in t.final_libs.copy():
579                 t2 = bld.name_to_obj(l, bld.env)
580                 if t.sname in t2.final_libs:
581                     # we could break this in either direction. If one of the libraries
582                     # has a version number, and will this be distributed publicly, then
583                     # we should make it the lower level library in the DAG
584                     debug('deps: removing library loop %s<->%s', t.sname, l)
585                     t2.final_libs.remove(t.sname)
586                     loops[t2.sname] = t.sname;
587
588     re_expand2(bld, tgt_list)
589
590     for type in ['BINARY']:
591         while True:
592             changed = False
593             for t in tgt_list:
594                 if t.samba_type != type: continue
595                 # if we will indirectly link to a target then we don't need it
596                 new = t.final_objects.copy()
597                 for l in t.final_libs:
598                     t2 = bld.name_to_obj(l, bld.env)
599                     dup = new.intersection(t2.expanded_targets2)
600                     if dup:
601                         debug('deps: removing dups from %s: %s also in %s %s',
602                               t.sname, dup, t2.samba_type, l)
603                         new = new.difference(dup)
604                         changed = True
605                 if changed:
606                     t.final_objects = new
607                     break
608             if not changed:
609                 break
610
611     # we now need to make corrections for any library loops we broke up
612     # any target that depended on the target of the loop and doesn't
613     # depend on the source of the loop needs to get the loop source added
614     for type in ['BINARY','PYTHON']:
615         for t in tgt_list:
616             if t.samba_type != type: continue
617             for loop in loops:
618                 if loop in t.final_libs and loops[loop] not in t.final_libs:
619                     t.final_libs.add(loops[loop])
620
621     debug('deps: removed duplicate dependencies')
622
623
624 ######################################################################
625 # this provides a way to save our dependency calculations between runs
626 savedeps_version = 3
627 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
628 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
629 savedeps_outenv  = ['INC_PATHS']
630 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
631 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
632
633 def save_samba_deps(bld, tgt_list):
634     '''save the dependency calculations between builds, to make
635        further builds faster'''
636     denv = Environment.Environment()
637
638     denv.version = savedeps_version
639     denv.savedeps_inputs = savedeps_inputs
640     denv.savedeps_outputs = savedeps_outputs
641     denv.input = {}
642     denv.output = {}
643     denv.outenv = {}
644     denv.caches = {}
645     denv.files  = {}
646
647     for f in savedeps_files:
648         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
649
650     for c in savedeps_caches:
651         denv.caches[c] = LOCAL_CACHE(bld, c)
652
653     for t in tgt_list:
654         # save all the input attributes for each target
655         tdeps = {}
656         for attr in savedeps_inputs:
657             v = getattr(t, attr, None)
658             if v is not None:
659                 tdeps[attr] = v
660         if tdeps != {}:
661             denv.input[t.sname] = tdeps
662
663         # save all the output attributes for each target
664         tdeps = {}
665         for attr in savedeps_outputs:
666             v = getattr(t, attr, None)
667             if v is not None:
668                 tdeps[attr] = v
669         if tdeps != {}:
670             denv.output[t.sname] = tdeps
671
672         tdeps = {}
673         for attr in savedeps_outenv:
674             if attr in t.env:
675                 tdeps[attr] = t.env[attr]
676         if tdeps != {}:
677             denv.outenv[t.sname] = tdeps
678
679     depsfile = os.path.join(bld.bdir, "sambadeps")
680     denv.store(depsfile)
681
682
683 def load_samba_deps(bld, tgt_list):
684     '''load a previous set of build dependencies if possible'''
685     depsfile = os.path.join(bld.bdir, "sambadeps")
686     denv = Environment.Environment()
687     try:
688         debug('deps: checking saved dependencies')
689         denv.load(depsfile)
690         if (denv.version != savedeps_version or
691             denv.savedeps_inputs != savedeps_inputs or
692             denv.savedeps_outputs != savedeps_outputs):
693             return False
694     except:
695         return False
696
697     # check if critical files have changed
698     for f in savedeps_files:
699         if f not in denv.files:
700             return False
701         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
702             return False
703
704     # check if caches are the same
705     for c in savedeps_caches:
706         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
707             return False
708
709     # check inputs are the same
710     for t in tgt_list:
711         tdeps = {}
712         for attr in savedeps_inputs:
713             v = getattr(t, attr, None)
714             if v is not None:
715                 tdeps[attr] = v
716         if t.sname in denv.input:
717             olddeps = denv.input[t.sname]
718         else:
719             olddeps = {}
720         if tdeps != olddeps:
721             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
722             return False
723
724     # put outputs in place
725     for t in tgt_list:
726         if not t.sname in denv.output: continue
727         tdeps = denv.output[t.sname]
728         for a in tdeps:
729             setattr(t, a, tdeps[a])
730
731     # put output env vars in place
732     for t in tgt_list:
733         if not t.sname in denv.outenv: continue
734         tdeps = denv.outenv[t.sname]
735         for a in tdeps:
736             t.env[a] = tdeps[a]
737
738     debug('deps: loaded saved dependencies')
739     return True
740
741
742 def check_project_rules(bld):
743     '''check the project rules - ensuring the targets are sane'''
744
745     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
746
747     # build a list of task generators we are interested in
748     tgt_list = []
749     for tgt in targets:
750         type = targets[tgt]
751         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
752             continue
753         t = bld.name_to_obj(tgt, bld.env)
754         if t is None:
755             print "Target %s of type %s has no task generator" % (tgt, type)
756             raise
757         tgt_list.append(t)
758
759     add_samba_attributes(bld, tgt_list)
760
761     if load_samba_deps(bld, tgt_list):
762         return
763
764     print "Checking project rules ..."
765
766     debug('deps: project rules checking started')
767
768     expand_subsystem_deps(bld)
769     build_direct_deps(bld, tgt_list)
770     build_indirect_deps(bld, tgt_list)
771     calculate_final_deps(bld, tgt_list)
772
773     # run the various attribute generators
774     for f in [ build_dependencies, build_includes, add_init_functions ]:
775         debug('deps: project rules checking %s', f)
776         for t in tgt_list: f(t)
777
778     debug('deps: project rules stage1 completed')
779
780     #check_orpaned_targets(bld, tgt_list)
781
782     if not check_duplicate_sources(bld, tgt_list):
783         print "Duplicate sources present - aborting"
784         sys.exit(1)
785
786     show_final_deps(bld, tgt_list)
787
788     debug('deps: project rules checking completed - %u targets checked',
789           len(tgt_list))
790
791     save_samba_deps(bld, tgt_list)
792
793     print "Project rules pass"
794
795
796 def CHECK_PROJECT_RULES(bld):
797     '''enable checking of project targets for sanity'''
798     if bld.env.added_project_rules:
799         return
800     bld.env.added_project_rules = True
801     bld.add_pre_fun(check_project_rules)
802 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
803
804