build: removed debug line
[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 @conf
26 def SET_SYSLIB_DEPS(conf, target, deps):
27     '''setup some implied dependencies for a SYSLIB'''
28     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
29     cache[target] = deps
30
31
32 def EXPAND_ALIAS(bld, target):
33     '''expand a target name via an alias'''
34     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
35     if target in aliases:
36         return aliases[target]
37     return target
38 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
39
40
41 def expand_subsystem_deps(bld):
42     '''expand the reverse dependencies resulting from subsystem
43        attributes of modules'''
44     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
45     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
46     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
47
48     for s in subsystems:
49         if s in aliases:
50             s = aliases[s]
51         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
52         type = targets[s]
53         if type == 'DISABLED' or type == 'EMPTY':
54             continue
55
56         t = bld.name_to_obj(s, bld.env)
57         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
58         for d in subsystems[s]:
59             type = targets[d['TARGET']]
60             if type != 'DISABLED' and type != 'EMPTY':
61                 t.samba_deps_extended.append(d['TARGET'])
62                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
63                 t2.samba_includes_extended.extend(t.samba_includes_extended)
64                 t2.samba_deps_extended.extend(t.samba_deps_extended)
65         t.samba_deps_extended = unique_list(t.samba_deps_extended)
66
67
68
69 def build_dependencies(self):
70     '''This builds the dependency list for a target. It runs after all the targets are declared
71
72     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
73     the full dependency list for a target until we have all of the targets declared.
74     '''
75
76     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
77         self.uselib        = list(self.final_syslibs)
78         self.uselib_local  = list(self.final_libs)
79         self.add_objects   = list(self.final_objects)
80
81         # extra link flags from pkg_config
82         libs = self.final_syslibs.copy()
83
84         (ccflags, ldflags) = library_flags(self, list(libs))
85         new_ldflags        = getattr(self, 'ldflags', [])
86         new_ldflags.extend(ldflags)
87         self.ldflags       = new_ldflags
88
89         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
90               self.sname, self.uselib, self.uselib_local, self.add_objects)
91
92     if self.samba_type in ['SUBSYSTEM']:
93         # this is needed for the ccflags of libs that come from pkg_config
94         self.uselib = list(self.direct_syslibs)
95
96     if getattr(self, 'uselib', None):
97         up_list = []
98         for l in self.uselib:
99             up_list.append(l.upper())
100         self.uselib = up_list
101
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_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                     print("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                 print "Target %s of type %s is unused by any other target" % (t.sname, type)
283
284
285 def show_final_deps(bld, tgt_list):
286     '''show the final dependencies for all targets'''
287
288     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
289
290     for t in tgt_list:
291         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
292             continue
293         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
294               t.sname, t.uselib, t.uselib_local, t.add_objects)
295
296
297 def add_samba_attributes(bld, tgt_list):
298     '''ensure a target has a the required samba attributes'''
299
300     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
301
302     for t in tgt_list:
303         if t.name != '':
304             t.sname = t.name
305         else:
306             t.sname = t.target
307         t.samba_type = targets[t.sname]
308         t.samba_abspath = t.path.abspath(bld.env)
309         t.samba_deps_extended = t.samba_deps[:]
310         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
311         t.ccflags = getattr(t, 'samba_cflags', '')
312
313
314 def build_direct_deps(bld, tgt_list):
315     '''build the direct_objects and direct_libs sets for each target'''
316
317     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
318     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
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 d == t.sname: continue
330             if not d in targets:
331                 print "Unknown dependency %s in %s" % (d, t.sname)
332                 raise
333             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
334                 continue
335             if targets[d] == 'SYSLIB':
336                 t.direct_syslibs.add(d)
337                 if d in syslib_deps:
338                     for implied in TO_LIST(syslib_deps[d]):
339                         t.direct_libs.add(implied)
340                 continue
341             t2 = bld.name_to_obj(d, bld.env)
342             if t2 is None:
343                 print "no task %s type %s" % (d, targets[d])
344             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
345                 t.direct_libs.add(d)
346             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
347                 t.direct_objects.add(d)
348     debug('deps: built direct dependencies')
349
350
351 def dependency_loop(loops, t, target):
352     '''add a dependency loop to the loops dictionary'''
353     if t.sname == target:
354         return
355     if not target in loops:
356         loops[target] = set()
357     if not t.sname in loops[target]:
358         loops[target].add(t.sname)
359
360
361 def indirect_libs(bld, t, chain, loops):
362     '''recursively calculate the indirect library dependencies for a target
363
364     An indirect library is a library that results from a dependency on
365     a subsystem
366     '''
367
368     ret = getattr(t, 'indirect_libs', None)
369     if ret is not None:
370         return ret
371
372     ret = set()
373     for obj in t.direct_objects:
374         if obj in chain:
375             dependency_loop(loops, t, obj)
376             continue
377         chain.add(obj)
378         t2 = bld.name_to_obj(obj, bld.env)
379         r2 = indirect_libs(bld, t2, chain, loops)
380         chain.remove(obj)
381         ret = ret.union(t2.direct_libs)
382         ret = ret.union(r2)
383
384     for obj in indirect_objects(bld, t, set(), loops):
385         if obj in chain:
386             dependency_loop(loops, t, obj)
387             continue
388         chain.add(obj)
389         t2 = bld.name_to_obj(obj, bld.env)
390         r2 = indirect_libs(bld, t2, chain, loops)
391         chain.remove(obj)
392         ret = ret.union(t2.direct_libs)
393         ret = ret.union(r2)
394
395     t.indirect_libs = ret
396
397     return ret
398
399
400 def indirect_objects(bld, t, chain, loops):
401     '''recursively calculate the indirect object dependencies for a target
402
403     indirect objects are the set of objects from expanding the
404     subsystem dependencies
405     '''
406
407     ret = getattr(t, 'indirect_objects', None)
408     if ret is not None: return ret
409
410     ret = set()
411     for lib in t.direct_objects:
412         if lib in chain:
413             dependency_loop(loops, t, lib)
414             continue
415         chain.add(lib)
416         t2 = bld.name_to_obj(lib, bld.env)
417         r2 = indirect_objects(bld, t2, chain, loops)
418         chain.remove(lib)
419         ret = ret.union(t2.direct_objects)
420         ret = ret.union(r2)
421
422     t.indirect_objects = ret
423     return ret
424
425
426 def extended_objects(bld, t, chain):
427     '''recursively calculate the extended object dependencies for a target
428
429     extended objects are the union of:
430        - direct objects
431        - indirect objects
432        - direct and indirect objects of all direct and indirect libraries
433     '''
434
435     ret = getattr(t, 'extended_objects', None)
436     if ret is not None: return ret
437
438     ret = set()
439     ret = ret.union(t.direct_objects)
440     ret = ret.union(t.indirect_objects)
441
442     for lib in t.direct_libs:
443         if lib in chain:
444             continue
445         t2 = bld.name_to_obj(lib, bld.env)
446         chain.add(lib)
447         r2 = extended_objects(bld, t2, chain)
448         chain.remove(lib)
449         ret = ret.union(t2.direct_objects)
450         ret = ret.union(t2.indirect_objects)
451         ret = ret.union(r2)
452
453     t.extended_objects = ret
454     return ret
455
456
457 def includes_objects(bld, t, chain, inc_loops):
458     '''recursively calculate the includes object dependencies for a target
459
460     includes dependencies come from either library or object dependencies
461     '''
462     ret = getattr(t, 'includes_objects', None)
463     if ret is not None:
464         return ret
465
466     ret = t.direct_objects.copy()
467     ret = ret.union(t.direct_libs)
468
469     for obj in t.direct_objects:
470         if obj in chain:
471             dependency_loop(inc_loops, t, obj)
472             continue
473         chain.add(obj)
474         t2 = bld.name_to_obj(obj, bld.env)
475         r2 = includes_objects(bld, t2, chain, inc_loops)
476         chain.remove(obj)
477         ret = ret.union(t2.direct_objects)
478         ret = ret.union(r2)
479
480     for lib in t.direct_libs:
481         if lib in chain:
482             dependency_loop(inc_loops, t, lib)
483             continue
484         chain.add(lib)
485         t2 = bld.name_to_obj(lib, bld.env)
486         r2 = includes_objects(bld, t2, chain, inc_loops)
487         chain.remove(lib)
488         ret = ret.union(t2.direct_objects)
489         ret = ret.union(r2)
490
491     t.includes_objects = ret
492     return ret
493
494
495 def break_dependency_loops(bld, tgt_list):
496     '''find and break dependency loops'''
497     loops = {}
498     inc_loops = {}
499
500     # build up the list of loops
501     for t in tgt_list:
502         indirect_objects(bld, t, set(), loops)
503         indirect_libs(bld, t, set(), loops)
504         includes_objects(bld, t, set(), inc_loops)
505
506     # break the loops
507     for t in tgt_list:
508         if t.sname in loops:
509             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
510                 objs = getattr(t, attr, set())
511                 setattr(t, attr, objs.difference(loops[t.sname]))
512
513     for loop in loops:
514         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
515
516     # expand the loops mapping by one level
517     for loop in loops.copy():
518         for tgt in loops[loop]:
519             if tgt in loops:
520                 loops[loop] = loops[loop].union(loops[tgt])
521
522     # expand indirect subsystem and library loops
523     for loop in loops.copy():
524         t = bld.name_to_obj(loop, bld.env)
525         if t.samba_type in ['SUBSYSTEM']:
526             loops[loop] = loops[loop].union(t.indirect_objects)
527             loops[loop] = loops[loop].union(t.direct_objects)
528         if t.samba_type in ['LIBRARY','PYTHON']:
529             loops[loop] = loops[loop].union(t.indirect_libs)
530             loops[loop] = loops[loop].union(t.direct_libs)
531         if loop in loops[loop]:
532             loops[loop].remove(loop)
533
534     # add in the replacement dependencies
535     for t in tgt_list:
536         for loop in loops:
537             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
538                 objs = getattr(t, attr, set())
539                 if loop in objs:
540                     diff = loops[loop].difference(objs)
541                     if t.sname in diff:
542                         diff.remove(t.sname)
543                     if diff:
544                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
545                         objs = objs.union(diff)
546                 if t.sname == 'ldb_password_hash':
547                     debug('deps: setting %s %s to %s', t.sname, attr, objs)
548                 setattr(t, attr, objs)
549
550 def calculate_final_deps(bld, tgt_list, loops):
551     '''calculate the final library and object dependencies'''
552     for t in tgt_list:
553         # start with the maximum possible list
554         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
555         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
556
557     for t in tgt_list:
558         # don't depend on ourselves
559         if t.sname in t.final_libs:
560             t.final_libs.remove(t.sname)
561         if t.sname in t.final_objects:
562             t.final_objects.remove(t.sname)
563
564     # find any library loops
565     for t in tgt_list:
566         if t.samba_type in ['LIBRARY', 'PYTHON']:
567             for l in t.final_libs.copy():
568                 t2 = bld.name_to_obj(l, bld.env)
569                 if t.sname in t2.final_libs:
570                     # we could break this in either direction. If one of the libraries
571                     # has a version number, and will this be distributed publicly, then
572                     # we should make it the lower level library in the DAG
573                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
574                     dependency_loop(loops, t, t2.sname)
575                     t2.final_libs.remove(t.sname)
576
577     for type in ['BINARY']:
578         for t in tgt_list:
579             if t.samba_type != type: continue
580             # if we will indirectly link to a target then we don't need it
581             new = t.final_objects.copy()
582             for l in t.final_libs:
583                 t2 = bld.name_to_obj(l, bld.env)
584                 t2_obj = extended_objects(bld, t2, set())
585                 dup = new.intersection(t2_obj)
586                 if dup:
587                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
588                           t.sname, t.samba_type, dup, t2.samba_type, l)
589                     new = new.difference(dup)
590                     changed = True
591             t.final_objects = new
592
593     for loop in loops:
594         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
595
596     # we now need to make corrections for any library loops we broke up
597     # any target that depended on the target of the loop and doesn't
598     # depend on the source of the loop needs to get the loop source added
599     for type in ['BINARY','PYTHON','LIBRARY']:
600         for t in tgt_list:
601             if t.samba_type != type: continue
602             for loop in loops:
603                 if loop in t.final_libs:
604                     diff = loops[loop].difference(t.final_libs)
605                     if t.sname in diff:
606                         diff.remove(t.sname)
607                     if diff:
608                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
609                         t.final_libs = t.final_libs.union(diff)
610
611     # add in any syslib dependencies
612     for t in tgt_list:
613         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
614             continue
615         syslibs = set()
616         for d in t.final_objects:
617             t2 = bld.name_to_obj(d, bld.env)
618             syslibs = syslibs.union(t2.direct_syslibs)
619         # this adds the indirect syslibs as well, which may not be needed
620         # depending on the linker flags
621         for d in t.final_libs:
622             t2 = bld.name_to_obj(d, bld.env)
623             syslibs = syslibs.union(t2.direct_syslibs)
624         t.final_syslibs = syslibs
625
626     debug('deps: removed duplicate dependencies')
627
628
629
630 ######################################################################
631 # this provides a way to save our dependency calculations between runs
632 savedeps_version = 3
633 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
634 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
635 savedeps_outenv  = ['INC_PATHS']
636 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
637 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
638
639 def save_samba_deps(bld, tgt_list):
640     '''save the dependency calculations between builds, to make
641        further builds faster'''
642     denv = Environment.Environment()
643
644     denv.version = savedeps_version
645     denv.savedeps_inputs = savedeps_inputs
646     denv.savedeps_outputs = savedeps_outputs
647     denv.input = {}
648     denv.output = {}
649     denv.outenv = {}
650     denv.caches = {}
651     denv.files  = {}
652
653     for f in savedeps_files:
654         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
655
656     for c in savedeps_caches:
657         denv.caches[c] = LOCAL_CACHE(bld, c)
658
659     for t in tgt_list:
660         # save all the input attributes for each target
661         tdeps = {}
662         for attr in savedeps_inputs:
663             v = getattr(t, attr, None)
664             if v is not None:
665                 tdeps[attr] = v
666         if tdeps != {}:
667             denv.input[t.sname] = tdeps
668
669         # save all the output attributes for each target
670         tdeps = {}
671         for attr in savedeps_outputs:
672             v = getattr(t, attr, None)
673             if v is not None:
674                 tdeps[attr] = v
675         if tdeps != {}:
676             denv.output[t.sname] = tdeps
677
678         tdeps = {}
679         for attr in savedeps_outenv:
680             if attr in t.env:
681                 tdeps[attr] = t.env[attr]
682         if tdeps != {}:
683             denv.outenv[t.sname] = tdeps
684
685     depsfile = os.path.join(bld.bdir, "sambadeps")
686     denv.store(depsfile)
687
688
689 def load_samba_deps(bld, tgt_list):
690     '''load a previous set of build dependencies if possible'''
691     depsfile = os.path.join(bld.bdir, "sambadeps")
692     denv = Environment.Environment()
693     try:
694         debug('deps: checking saved dependencies')
695         denv.load(depsfile)
696         if (denv.version != savedeps_version or
697             denv.savedeps_inputs != savedeps_inputs or
698             denv.savedeps_outputs != savedeps_outputs):
699             return False
700     except:
701         return False
702
703     # check if critical files have changed
704     for f in savedeps_files:
705         if f not in denv.files:
706             return False
707         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
708             return False
709
710     # check if caches are the same
711     for c in savedeps_caches:
712         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
713             return False
714
715     # check inputs are the same
716     for t in tgt_list:
717         tdeps = {}
718         for attr in savedeps_inputs:
719             v = getattr(t, attr, None)
720             if v is not None:
721                 tdeps[attr] = v
722         if t.sname in denv.input:
723             olddeps = denv.input[t.sname]
724         else:
725             olddeps = {}
726         if tdeps != olddeps:
727             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
728             return False
729
730     # put outputs in place
731     for t in tgt_list:
732         if not t.sname in denv.output: continue
733         tdeps = denv.output[t.sname]
734         for a in tdeps:
735             setattr(t, a, tdeps[a])
736
737     # put output env vars in place
738     for t in tgt_list:
739         if not t.sname in denv.outenv: continue
740         tdeps = denv.outenv[t.sname]
741         for a in tdeps:
742             t.env[a] = tdeps[a]
743
744     debug('deps: loaded saved dependencies')
745     return True
746
747
748 def check_project_rules(bld):
749     '''check the project rules - ensuring the targets are sane'''
750
751     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
752     loops = {}
753     inc_loops = {}
754
755     # build a list of task generators we are interested in
756     tgt_list = []
757     for tgt in targets:
758         type = targets[tgt]
759         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
760             continue
761         t = bld.name_to_obj(tgt, bld.env)
762         if t is None:
763             print "Target %s of type %s has no task generator" % (tgt, type)
764             raise
765         tgt_list.append(t)
766
767     add_samba_attributes(bld, tgt_list)
768
769     if load_samba_deps(bld, tgt_list):
770         return
771
772     print "Checking project rules ..."
773
774     debug('deps: project rules checking started')
775
776     expand_subsystem_deps(bld)
777     build_direct_deps(bld, tgt_list)
778     break_dependency_loops(bld, tgt_list)
779     calculate_final_deps(bld, tgt_list, loops)
780
781     # run the various attribute generators
782     for f in [ build_dependencies, build_includes, add_init_functions ]:
783         debug('deps: project rules checking %s', f)
784         for t in tgt_list: f(t)
785
786     debug('deps: project rules stage1 completed')
787
788     #check_orpaned_targets(bld, tgt_list)
789
790     if not check_duplicate_sources(bld, tgt_list):
791         print "Duplicate sources present - aborting"
792         sys.exit(1)
793
794     show_final_deps(bld, tgt_list)
795
796     debug('deps: project rules checking completed - %u targets checked',
797           len(tgt_list))
798
799     save_samba_deps(bld, tgt_list)
800
801     print "Project rules pass"
802
803
804 def CHECK_PROJECT_RULES(bld):
805     '''enable checking of project targets for sanity'''
806     if bld.env.added_project_rules:
807         return
808     bld.env.added_project_rules = True
809     bld.add_pre_fun(check_project_rules)
810 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
811
812