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