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