build: python 2.4 doesn't support multiple union arguments for sets
[nivanova/samba-autobuild/.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 = includes_objects(bld, self, set(), {})
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
316 def build_direct_deps(bld, tgt_list):
317     '''build the direct_objects and direct_libs sets for each target'''
318
319     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
320     global_deps = bld.env.GLOBAL_DEPENDENCIES
321
322     for t in tgt_list:
323         t.direct_objects = set()
324         t.direct_libs = set()
325         t.direct_syslibs = set()
326         deps = t.samba_deps_extended
327         deps.extend(global_deps)
328         for d in deps:
329             d = EXPAND_ALIAS(bld, d)
330             if d == t.sname: continue
331             if not d in targets:
332                 print "Unknown dependency %s in %s" % (d, t.sname)
333                 raise
334             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
335                 continue
336             if targets[d] == 'SYSLIB':
337                 t.direct_syslibs.add(d)
338                 continue
339             t2 = bld.name_to_obj(d, bld.env)
340             if t2 is None:
341                 print "no task %s type %s" % (d, targets[d])
342             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
343                 t.direct_libs.add(d)
344             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
345                 t.direct_objects.add(d)
346     debug('deps: built direct dependencies')
347
348
349 def dependency_loop(loops, t, target):
350     '''add a dependency loop to the loops dictionary'''
351     if t.sname == target:
352         return
353     if not target in loops:
354         loops[target] = set()
355     if not t.sname in loops[target]:
356         loops[target].add(t.sname)
357
358
359 def indirect_libs(bld, t, chain, loops):
360     '''recursively calculate the indirect library dependencies for a target
361
362     An indirect library is a library that results from a dependency on
363     a subsystem
364     '''
365
366     ret = getattr(t, 'indirect_libs', None)
367     if ret is not None:
368         return ret
369
370     ret = set()
371     for obj in t.direct_objects:
372         if obj in chain:
373             dependency_loop(loops, t, obj)
374             continue
375         chain.add(obj)
376         t2 = bld.name_to_obj(obj, bld.env)
377         r2 = indirect_libs(bld, t2, chain, loops)
378         chain.remove(obj)
379         ret = ret.union(t2.direct_libs)
380         ret = ret.union(r2)
381
382     for obj in indirect_objects(bld, t, set(), loops):
383         if obj in chain:
384             dependency_loop(loops, t, obj)
385             continue
386         chain.add(obj)
387         t2 = bld.name_to_obj(obj, bld.env)
388         r2 = indirect_libs(bld, t2, chain, loops)
389         chain.remove(obj)
390         ret = ret.union(t2.direct_libs)
391         ret = ret.union(r2)
392
393     t.indirect_libs = ret
394
395     return ret
396
397
398 def indirect_syslibs(bld, t, chain, loops):
399     '''recursively calculate the indirect system library dependencies for a target
400
401     An indirect syslib results from a subsystem dependency
402     '''
403
404     ret = getattr(t, 'indirect_syslibs', None)
405     if ret is not None:
406         return ret
407
408     ret = set()
409     for obj in t.direct_objects:
410         if obj in chain:
411             dependency_loop(loops, t, obj)
412             continue
413         chain.add(obj)
414         t2 = bld.name_to_obj(obj, bld.env)
415         r2 = indirect_syslibs(bld, t2, chain, loops)
416         chain.remove(obj)
417         ret = ret.union(t2.direct_syslibs)
418         ret = ret.union(r2)
419
420     t.indirect_syslibs = ret
421     return ret
422
423
424 def indirect_objects(bld, t, chain, loops):
425     '''recursively calculate the indirect object dependencies for a target
426
427     indirect objects are the set of objects from expanding the
428     subsystem dependencies
429     '''
430
431     ret = getattr(t, 'indirect_objects', None)
432     if ret is not None: return ret
433
434     ret = set()
435     for lib in t.direct_objects:
436         if lib in chain:
437             dependency_loop(loops, t, lib)
438             continue
439         chain.add(lib)
440         t2 = bld.name_to_obj(lib, bld.env)
441         r2 = indirect_objects(bld, t2, chain, loops)
442         chain.remove(lib)
443         ret = ret.union(t2.direct_objects)
444         ret = ret.union(r2)
445
446     t.indirect_objects = ret
447     return ret
448
449
450 def extended_objects(bld, t, chain):
451     '''recursively calculate the extended object dependencies for a target
452
453     extended objects are the union of:
454        - direct objects
455        - indirect objects
456        - direct and indirect objects of all direct and indirect libraries
457     '''
458
459     ret = getattr(t, 'extended_objects', None)
460     if ret is not None: return ret
461
462     ret = set()
463     ret = ret.union(t.direct_objects)
464     ret = ret.union(t.indirect_objects)
465
466     for lib in t.direct_libs:
467         if lib in chain:
468             continue
469         t2 = bld.name_to_obj(lib, bld.env)
470         chain.add(lib)
471         r2 = extended_objects(bld, t2, chain)
472         chain.remove(lib)
473         ret = ret.union(t2.direct_objects)
474         ret = ret.union(t2.indirect_objects)
475         ret = ret.union(r2)
476
477     t.extended_objects = ret
478     return ret
479
480
481 def includes_objects(bld, t, chain, inc_loops):
482     '''recursively calculate the includes object dependencies for a target
483
484     includes dependencies come from either library or object dependencies
485     '''
486     ret = getattr(t, 'includes_objects', None)
487     if ret is not None:
488         return ret
489
490     ret = t.direct_objects.copy()
491     ret = ret.union(t.direct_libs)
492
493     for obj in t.direct_objects:
494         if obj in chain:
495             dependency_loop(inc_loops, t, obj)
496             continue
497         chain.add(obj)
498         t2 = bld.name_to_obj(obj, bld.env)
499         r2 = includes_objects(bld, t2, chain, inc_loops)
500         chain.remove(obj)
501         ret = ret.union(t2.direct_objects)
502         ret = ret.union(r2)
503
504     for lib in t.direct_libs:
505         if lib in chain:
506             dependency_loop(inc_loops, t, lib)
507             continue
508         chain.add(lib)
509         t2 = bld.name_to_obj(lib, bld.env)
510         r2 = includes_objects(bld, t2, chain, inc_loops)
511         chain.remove(lib)
512         ret = ret.union(t2.direct_objects)
513         ret = ret.union(r2)
514
515     t.includes_objects = ret
516     return ret
517
518
519 def break_dependency_loops(bld, tgt_list):
520     '''find and break dependency loops'''
521     loops = {}
522     inc_loops = {}
523
524     # build up the list of loops
525     for t in tgt_list:
526         indirect_objects(bld, t, set(), loops)
527         indirect_libs(bld, t, set(), loops)
528         includes_objects(bld, t, set(), inc_loops)
529
530     # break the loops
531     for t in tgt_list:
532         if t.sname in loops:
533             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
534                 objs = getattr(t, attr, set())
535                 setattr(t, attr, objs.difference(loops[t.sname]))
536
537     for loop in loops:
538         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
539
540     # expand the loops mapping by one level
541     for loop in loops.copy():
542         for tgt in loops[loop]:
543             if tgt in loops:
544                 loops[loop] = loops[loop].union(loops[tgt])
545
546     # expand indirect subsystem and library loops
547     for loop in loops.copy():
548         t = bld.name_to_obj(loop, bld.env)
549         if t.samba_type in ['SUBSYSTEM']:
550             loops[loop] = loops[loop].union(t.indirect_objects)
551             loops[loop] = loops[loop].union(t.direct_objects)
552         if t.samba_type in ['LIBRARY']:
553             loops[loop] = loops[loop].union(t.indirect_libs)
554             loops[loop] = loops[loop].union(t.direct_libs)
555         if loop in loops[loop]:
556             loops[loop].remove(loop)
557
558     # add in the replacement dependencies
559     for t in tgt_list:
560         for loop in loops:
561             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
562                 objs = getattr(t, attr, set())
563                 if loop in objs:
564                     diff = loops[loop].difference(objs)
565                     if t.sname in diff:
566                         diff.remove(t.sname)
567                     if diff:
568                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
569                         objs = objs.union(diff)
570                 if t.sname == 'ldb_password_hash':
571                     debug('deps: setting %s %s to %s', t.sname, attr, objs)
572                 setattr(t, attr, objs)
573
574     # now calculate the indirect syslibs, which can change from the loop expansion
575     for t in tgt_list:
576         indirect_syslibs(bld, t, set(), loops)
577
578
579 def calculate_final_deps(bld, tgt_list, loops):
580     '''calculate the final library and object dependencies'''
581     for t in tgt_list:
582         # start with the maximum possible list
583         t.final_syslibs = t.direct_syslibs.union(indirect_syslibs(bld, t, set(), loops))
584         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
585         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
586
587     for t in tgt_list:
588         # don't depend on ourselves
589         if t.sname in t.final_libs:
590             t.final_libs.remove(t.sname)
591         if t.sname in t.final_objects:
592             t.final_objects.remove(t.sname)
593
594     # find any library loops
595     for t in tgt_list:
596         if t.samba_type in ['LIBRARY', 'PYTHON']:
597             for l in t.final_libs.copy():
598                 t2 = bld.name_to_obj(l, bld.env)
599                 if t.sname in t2.final_libs:
600                     # we could break this in either direction. If one of the libraries
601                     # has a version number, and will this be distributed publicly, then
602                     # we should make it the lower level library in the DAG
603                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
604                     dependency_loop(loops, t, t2.sname)
605                     t2.final_libs.remove(t.sname)
606
607     for type in ['BINARY']:
608         for t in tgt_list:
609             if t.samba_type != type: continue
610             # if we will indirectly link to a target then we don't need it
611             new = t.final_objects.copy()
612             for l in t.final_libs:
613                 t2 = bld.name_to_obj(l, bld.env)
614                 t2_obj = extended_objects(bld, t2, set())
615                 dup = new.intersection(t2_obj)
616                 if dup:
617                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
618                           t.sname, t.samba_type, dup, t2.samba_type, l)
619                     new = new.difference(dup)
620                     changed = True
621             t.final_objects = new
622
623     for loop in loops:
624         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
625
626     # we now need to make corrections for any library loops we broke up
627     # any target that depended on the target of the loop and doesn't
628     # depend on the source of the loop needs to get the loop source added
629     for type in ['BINARY','PYTHON','LIBRARY']:
630         for t in tgt_list:
631             if t.samba_type != type: continue
632             for loop in loops:
633                 if loop in t.final_libs:
634                     diff = loops[loop].difference(t.final_libs)
635                     if t.sname in diff:
636                         diff.remove(t.sname)
637                     if diff:
638                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
639                         t.final_libs = t.final_libs.union(diff)
640
641     debug('deps: removed duplicate dependencies')
642
643
644 ######################################################################
645 # this provides a way to save our dependency calculations between runs
646 savedeps_version = 3
647 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
648 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
649 savedeps_outenv  = ['INC_PATHS']
650 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
651 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
652
653 def save_samba_deps(bld, tgt_list):
654     '''save the dependency calculations between builds, to make
655        further builds faster'''
656     denv = Environment.Environment()
657
658     denv.version = savedeps_version
659     denv.savedeps_inputs = savedeps_inputs
660     denv.savedeps_outputs = savedeps_outputs
661     denv.input = {}
662     denv.output = {}
663     denv.outenv = {}
664     denv.caches = {}
665     denv.files  = {}
666
667     for f in savedeps_files:
668         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
669
670     for c in savedeps_caches:
671         denv.caches[c] = LOCAL_CACHE(bld, c)
672
673     for t in tgt_list:
674         # save all the input attributes for each target
675         tdeps = {}
676         for attr in savedeps_inputs:
677             v = getattr(t, attr, None)
678             if v is not None:
679                 tdeps[attr] = v
680         if tdeps != {}:
681             denv.input[t.sname] = tdeps
682
683         # save all the output attributes for each target
684         tdeps = {}
685         for attr in savedeps_outputs:
686             v = getattr(t, attr, None)
687             if v is not None:
688                 tdeps[attr] = v
689         if tdeps != {}:
690             denv.output[t.sname] = tdeps
691
692         tdeps = {}
693         for attr in savedeps_outenv:
694             if attr in t.env:
695                 tdeps[attr] = t.env[attr]
696         if tdeps != {}:
697             denv.outenv[t.sname] = tdeps
698
699     depsfile = os.path.join(bld.bdir, "sambadeps")
700     denv.store(depsfile)
701
702
703 def load_samba_deps(bld, tgt_list):
704     '''load a previous set of build dependencies if possible'''
705     depsfile = os.path.join(bld.bdir, "sambadeps")
706     denv = Environment.Environment()
707     try:
708         debug('deps: checking saved dependencies')
709         denv.load(depsfile)
710         if (denv.version != savedeps_version or
711             denv.savedeps_inputs != savedeps_inputs or
712             denv.savedeps_outputs != savedeps_outputs):
713             return False
714     except:
715         return False
716
717     # check if critical files have changed
718     for f in savedeps_files:
719         if f not in denv.files:
720             return False
721         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
722             return False
723
724     # check if caches are the same
725     for c in savedeps_caches:
726         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
727             return False
728
729     # check inputs are the same
730     for t in tgt_list:
731         tdeps = {}
732         for attr in savedeps_inputs:
733             v = getattr(t, attr, None)
734             if v is not None:
735                 tdeps[attr] = v
736         if t.sname in denv.input:
737             olddeps = denv.input[t.sname]
738         else:
739             olddeps = {}
740         if tdeps != olddeps:
741             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
742             return False
743
744     # put outputs in place
745     for t in tgt_list:
746         if not t.sname in denv.output: continue
747         tdeps = denv.output[t.sname]
748         for a in tdeps:
749             setattr(t, a, tdeps[a])
750
751     # put output env vars in place
752     for t in tgt_list:
753         if not t.sname in denv.outenv: continue
754         tdeps = denv.outenv[t.sname]
755         for a in tdeps:
756             t.env[a] = tdeps[a]
757
758     debug('deps: loaded saved dependencies')
759     return True
760
761
762 def check_project_rules(bld):
763     '''check the project rules - ensuring the targets are sane'''
764
765     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
766     loops = {}
767     inc_loops = {}
768
769     # build a list of task generators we are interested in
770     tgt_list = []
771     for tgt in targets:
772         type = targets[tgt]
773         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
774             continue
775         t = bld.name_to_obj(tgt, bld.env)
776         if t is None:
777             print "Target %s of type %s has no task generator" % (tgt, type)
778             raise
779         tgt_list.append(t)
780
781     add_samba_attributes(bld, tgt_list)
782
783     if load_samba_deps(bld, tgt_list):
784         return
785
786     print "Checking project rules ..."
787
788     debug('deps: project rules checking started')
789
790     expand_subsystem_deps(bld)
791     build_direct_deps(bld, tgt_list)
792     break_dependency_loops(bld, tgt_list)
793     calculate_final_deps(bld, tgt_list, loops)
794
795     # run the various attribute generators
796     for f in [ build_dependencies, build_includes, add_init_functions ]:
797         debug('deps: project rules checking %s', f)
798         for t in tgt_list: f(t)
799
800     debug('deps: project rules stage1 completed')
801
802     #check_orpaned_targets(bld, tgt_list)
803
804     if not check_duplicate_sources(bld, tgt_list):
805         print "Duplicate sources present - aborting"
806         sys.exit(1)
807
808     show_final_deps(bld, tgt_list)
809
810     debug('deps: project rules checking completed - %u targets checked',
811           len(tgt_list))
812
813     save_samba_deps(bld, tgt_list)
814
815     print "Project rules pass"
816
817
818 def CHECK_PROJECT_RULES(bld):
819     '''enable checking of project targets for sanity'''
820     if bld.env.added_project_rules:
821         return
822     bld.env.added_project_rules = True
823     bld.add_pre_fun(check_project_rules)
824 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
825
826