ef0bc793ae2d71e53e8ea2e977f71de711722cc1
[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 from samba_bundled import BUILTIN_LIBRARY
7
8 @conf
9 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
10     '''add a dependency for all binaries and libraries'''
11     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
12         ctx.env.GLOBAL_DEPENDENCIES = []
13     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
14
15
16 def TARGET_ALIAS(bld, target, alias):
17     '''define an alias for a target name'''
18     cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
19     if alias in cache:
20         print("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
21         raise
22     cache[alias] = target
23 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
24
25
26 @conf
27 def SET_SYSLIB_DEPS(conf, target, deps):
28     '''setup some implied dependencies for a SYSLIB'''
29     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
30     cache[target] = deps
31
32
33 def EXPAND_ALIAS(bld, target):
34     '''expand a target name via an alias'''
35     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
36     if target in aliases:
37         return aliases[target]
38     return target
39 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
40
41
42 def expand_subsystem_deps(bld):
43     '''expand the reverse dependencies resulting from subsystem
44        attributes of modules'''
45     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
46     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
47     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
48
49     for s in subsystems:
50         if s in aliases:
51             s = aliases[s]
52         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
53         type = targets[s]
54         if type == 'DISABLED' or type == 'EMPTY':
55             continue
56
57         t = bld.name_to_obj(s, bld.env)
58         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
59         for d in subsystems[s]:
60             type = targets[d['TARGET']]
61             if type != 'DISABLED' and type != 'EMPTY':
62                 t.samba_deps_extended.append(d['TARGET'])
63                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
64                 t2.samba_includes_extended.extend(t.samba_includes_extended)
65                 t2.samba_deps_extended.extend(t.samba_deps_extended)
66         t.samba_deps_extended = unique_list(t.samba_deps_extended)
67
68
69
70 def build_dependencies(self):
71     '''This builds the dependency list for a target. It runs after all the targets are declared
72
73     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
74     the full dependency list for a target until we have all of the targets declared.
75     '''
76
77     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
78         self.uselib        = list(self.final_syslibs)
79         self.uselib_local  = list(self.final_libs)
80         self.add_objects   = list(self.final_objects)
81
82         # extra link flags from pkg_config
83         libs = self.final_syslibs.copy()
84
85         (ccflags, ldflags) = library_flags(self, list(libs))
86         new_ldflags        = getattr(self, 'ldflags', [])
87         new_ldflags.extend(ldflags)
88         self.ldflags       = new_ldflags
89
90         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
91               self.sname, self.uselib, self.uselib_local, self.add_objects)
92
93     if self.samba_type in ['SUBSYSTEM']:
94         # this is needed for the ccflags of libs that come from pkg_config
95         self.uselib = list(self.direct_syslibs)
96
97     if getattr(self, 'uselib', None):
98         up_list = []
99         for l in self.uselib:
100             up_list.append(l.upper())
101         self.uselib = up_list
102
103
104 def build_includes(self):
105     '''This builds the right set of includes for a target.
106
107     One tricky part of this is that the includes= attribute for a
108     target needs to use paths which are relative to that targets
109     declaration directory (which we can get at via t.path).
110
111     The way this works is the includes list gets added as
112     samba_includes in the main build task declaration. Then this
113     function runs after all of the tasks are declared, and it
114     processes the samba_includes attribute to produce a includes=
115     attribute
116     '''
117
118     if getattr(self, 'samba_includes', None) is None:
119         return
120
121     bld = self.bld
122
123     inc_deps = includes_objects(bld, self, set(), {})
124
125     includes = []
126
127     # maybe add local includes
128     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
129         includes.append('.')
130
131     includes.extend(self.samba_includes_extended)
132
133     if 'EXTRA_INCLUDES' in bld.env:
134         includes.extend(bld.env['EXTRA_INCLUDES'])
135
136     includes.append('#')
137
138     inc_set = set()
139     inc_abs = []
140
141     for d in inc_deps:
142         t = bld.name_to_obj(d, bld.env)
143         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
144         inclist = getattr(t, 'samba_includes_extended', [])
145         if getattr(t, 'local_include', True) == True:
146             inclist.append('.')
147         if inclist == []:
148             continue
149         tpath = t.samba_abspath
150         for inc in inclist:
151             npath = tpath + '/' + inc
152             if not npath in inc_set:
153                 inc_abs.append(npath)
154                 inc_set.add(npath)
155
156     mypath = self.path.abspath(bld.env)
157     for inc in inc_abs:
158         relpath = os_path_relpath(inc, mypath)
159         includes.append(relpath)
160
161     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
162         includes.append('.')
163
164     # now transform the includes list to be relative to the top directory
165     # which is represented by '#' in waf. This allows waf to cache the
166     # includes lists more efficiently
167     includes_top = []
168     for i in includes:
169         if i[0] == '#':
170             # some are already top based
171             includes_top.append(i)
172             continue
173         absinc = os.path.join(self.path.abspath(), i)
174         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
175         includes_top.append('#' + relinc)
176
177     self.includes = unique_list(includes_top)
178     debug('deps: includes for target %s: includes=%s',
179           self.sname, self.includes)
180
181
182
183
184 def add_init_functions(self):
185     '''This builds the right set of init functions'''
186
187     bld = self.bld
188
189     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
190
191     # cope with the separated object lists from BINARY and LIBRARY targets
192     sname = self.sname
193     if sname.endswith('.objlist'):
194         sname = sname[0:-8]
195
196     modules = []
197     if sname in subsystems:
198         modules.append(sname)
199
200     m = getattr(self, 'samba_modules', None)
201     if m is not None:
202         modules.extend(TO_LIST(m))
203
204     m = getattr(self, 'samba_subsystem', None)
205     if m is not None:
206         modules.append(m)
207
208     if modules == []:
209         return
210
211     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
212
213     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
214
215     cflags = getattr(self, 'samba_cflags', [])[:]
216     for m in modules:
217         bld.ASSERT(m in subsystems,
218                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
219         init_fn_list = []
220         for d in subsystems[m]:
221             if targets[d['TARGET']] != 'DISABLED':
222                 init_fn_list.append(d['INIT_FUNCTION'])
223         if init_fn_list == []:
224             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
225         else:
226             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
227     self.ccflags = cflags
228
229
230
231 def check_duplicate_sources(bld, tgt_list):
232     '''see if we are compiling the same source file into multiple
233     subsystem targets for the same library or binary'''
234
235     debug('deps: checking for duplicate sources')
236
237     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
238     ret = True
239
240     seen = set()
241
242     for t in tgt_list:
243         obj_sources = getattr(t, 'source', '')
244         tpath = os_path_relpath(t.path.abspath(bld.env), t.env['BUILD_DIRECTORY'] + '/default')
245         obj_sources = bld.SUBDIR(tpath, obj_sources)
246         t.samba_source_set = set(TO_LIST(obj_sources))
247
248     for t in tgt_list:
249         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
250             continue
251
252         sources = []
253         for obj in t.add_objects:
254             t2 = t.bld.name_to_obj(obj, bld.env)
255             source_set = getattr(t2, 'samba_source_set', set())
256             sources.append( { 'dep':obj, 'src':source_set} )
257         for s in sources:
258             for s2 in sources:
259                 if s['dep'] == s2['dep']: continue
260                 common = s['src'].intersection(s2['src'])
261                 if common.difference(seen):
262                     print("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
263                                                                                       s['dep'], s2['dep'],
264                                                                                       common))
265                     seen = seen.union(common)
266                     ret = False
267     return ret
268
269
270 def check_orpaned_targets(bld, tgt_list):
271     '''check if any build targets are orphaned'''
272
273     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
274
275     debug('deps: checking for orphaned targets')
276
277     for t in tgt_list:
278         if getattr(t, 'samba_used', False) == True:
279             continue
280         type = target_dict[t.sname]
281         if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
282             if re.search('^PIDL_', t.sname) is None:
283                 print "Target %s of type %s is unused by any other target" % (t.sname, type)
284
285
286 def show_final_deps(bld, tgt_list):
287     '''show the final dependencies for all targets'''
288
289     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
290
291     for t in tgt_list:
292         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
293             continue
294         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
295               t.sname, t.uselib, t.uselib_local, t.add_objects)
296
297
298 def add_samba_attributes(bld, tgt_list):
299     '''ensure a target has a the required samba attributes'''
300
301     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
302
303     for t in tgt_list:
304         if t.name != '':
305             t.sname = t.name
306         else:
307             t.sname = t.target
308         t.samba_type = targets[t.sname]
309         t.samba_abspath = t.path.abspath(bld.env)
310         t.samba_deps_extended = t.samba_deps[:]
311         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
312         t.ccflags = getattr(t, 'samba_cflags', '')
313
314
315 def build_direct_deps(bld, tgt_list):
316     '''build the direct_objects and direct_libs sets for each target'''
317
318     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
319     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
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                 if d in syslib_deps:
339                     for implied in TO_LIST(syslib_deps[d]):
340                         if BUILTIN_LIBRARY(bld, implied):
341                             t.direct_objects.add(implied)
342                         else:
343                             t.direct_libs.add(implied)
344                 continue
345             t2 = bld.name_to_obj(d, bld.env)
346             if t2 is None:
347                 print "no task %s type %s" % (d, targets[d])
348             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
349                 t.direct_libs.add(d)
350             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
351                 t.direct_objects.add(d)
352     debug('deps: built direct dependencies')
353
354
355 def dependency_loop(loops, t, target):
356     '''add a dependency loop to the loops dictionary'''
357     if t.sname == target:
358         return
359     if not target in loops:
360         loops[target] = set()
361     if not t.sname in loops[target]:
362         loops[target].add(t.sname)
363
364
365 def indirect_libs(bld, t, chain, loops):
366     '''recursively calculate the indirect library dependencies for a target
367
368     An indirect library is a library that results from a dependency on
369     a subsystem
370     '''
371
372     ret = getattr(t, 'indirect_libs', None)
373     if ret is not None:
374         return ret
375
376     ret = set()
377     for obj in t.direct_objects:
378         if obj in chain:
379             dependency_loop(loops, t, obj)
380             continue
381         chain.add(obj)
382         t2 = bld.name_to_obj(obj, bld.env)
383         r2 = indirect_libs(bld, t2, chain, loops)
384         chain.remove(obj)
385         ret = ret.union(t2.direct_libs)
386         ret = ret.union(r2)
387
388     for obj in indirect_objects(bld, t, set(), loops):
389         if obj in chain:
390             dependency_loop(loops, t, obj)
391             continue
392         chain.add(obj)
393         t2 = bld.name_to_obj(obj, bld.env)
394         r2 = indirect_libs(bld, t2, chain, loops)
395         chain.remove(obj)
396         ret = ret.union(t2.direct_libs)
397         ret = ret.union(r2)
398
399     t.indirect_libs = ret
400
401     return ret
402
403
404 def indirect_objects(bld, t, chain, loops):
405     '''recursively calculate the indirect object dependencies for a target
406
407     indirect objects are the set of objects from expanding the
408     subsystem dependencies
409     '''
410
411     ret = getattr(t, 'indirect_objects', None)
412     if ret is not None: return ret
413
414     ret = set()
415     for lib in t.direct_objects:
416         if lib in chain:
417             dependency_loop(loops, t, lib)
418             continue
419         chain.add(lib)
420         t2 = bld.name_to_obj(lib, bld.env)
421         r2 = indirect_objects(bld, t2, chain, loops)
422         chain.remove(lib)
423         ret = ret.union(t2.direct_objects)
424         ret = ret.union(r2)
425
426     t.indirect_objects = ret
427     return ret
428
429
430 def extended_objects(bld, t, chain):
431     '''recursively calculate the extended object dependencies for a target
432
433     extended objects are the union of:
434        - direct objects
435        - indirect objects
436        - direct and indirect objects of all direct and indirect libraries
437     '''
438
439     ret = getattr(t, 'extended_objects', None)
440     if ret is not None: return ret
441
442     ret = set()
443     ret = ret.union(t.direct_objects)
444     ret = ret.union(t.indirect_objects)
445
446     for lib in t.direct_libs:
447         if lib in chain:
448             continue
449         t2 = bld.name_to_obj(lib, bld.env)
450         chain.add(lib)
451         r2 = extended_objects(bld, t2, chain)
452         chain.remove(lib)
453         ret = ret.union(t2.direct_objects)
454         ret = ret.union(t2.indirect_objects)
455         ret = ret.union(r2)
456
457     t.extended_objects = ret
458     return ret
459
460
461 def includes_objects(bld, t, chain, inc_loops):
462     '''recursively calculate the includes object dependencies for a target
463
464     includes dependencies come from either library or object dependencies
465     '''
466     ret = getattr(t, 'includes_objects', None)
467     if ret is not None:
468         return ret
469
470     ret = t.direct_objects.copy()
471     ret = ret.union(t.direct_libs)
472
473     for obj in t.direct_objects:
474         if obj in chain:
475             dependency_loop(inc_loops, t, obj)
476             continue
477         chain.add(obj)
478         t2 = bld.name_to_obj(obj, bld.env)
479         r2 = includes_objects(bld, t2, chain, inc_loops)
480         chain.remove(obj)
481         ret = ret.union(t2.direct_objects)
482         ret = ret.union(r2)
483
484     for lib in t.direct_libs:
485         if lib in chain:
486             dependency_loop(inc_loops, t, lib)
487             continue
488         chain.add(lib)
489         t2 = bld.name_to_obj(lib, bld.env)
490         r2 = includes_objects(bld, t2, chain, inc_loops)
491         chain.remove(lib)
492         ret = ret.union(t2.direct_objects)
493         ret = ret.union(r2)
494
495     t.includes_objects = ret
496     return ret
497
498
499 def break_dependency_loops(bld, tgt_list):
500     '''find and break dependency loops'''
501     loops = {}
502     inc_loops = {}
503
504     # build up the list of loops
505     for t in tgt_list:
506         indirect_objects(bld, t, set(), loops)
507         indirect_libs(bld, t, set(), loops)
508         includes_objects(bld, t, set(), inc_loops)
509
510     # break the loops
511     for t in tgt_list:
512         if t.sname in loops:
513             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
514                 objs = getattr(t, attr, set())
515                 setattr(t, attr, objs.difference(loops[t.sname]))
516
517     for loop in loops:
518         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
519
520     for loop in inc_loops:
521         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
522
523     # expand the loops mapping by one level
524     for loop in loops.copy():
525         for tgt in loops[loop]:
526             if tgt in loops:
527                 loops[loop] = loops[loop].union(loops[tgt])
528
529     for loop in inc_loops.copy():
530         for tgt in inc_loops[loop]:
531             if tgt in inc_loops:
532                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
533
534
535     # expand indirect subsystem and library loops
536     for loop in loops.copy():
537         t = bld.name_to_obj(loop, bld.env)
538         if t.samba_type in ['SUBSYSTEM']:
539             loops[loop] = loops[loop].union(t.indirect_objects)
540             loops[loop] = loops[loop].union(t.direct_objects)
541         if t.samba_type in ['LIBRARY','PYTHON']:
542             loops[loop] = loops[loop].union(t.indirect_libs)
543             loops[loop] = loops[loop].union(t.direct_libs)
544         if loop in loops[loop]:
545             loops[loop].remove(loop)
546
547     # expand indirect includes loops
548     for loop in inc_loops.copy():
549         t = bld.name_to_obj(loop, bld.env)
550         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
551         if loop in inc_loops[loop]:
552             inc_loops[loop].remove(loop)
553
554     # add in the replacement dependencies
555     for t in tgt_list:
556         for loop in loops:
557             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
558                 objs = getattr(t, attr, set())
559                 if loop in objs:
560                     diff = loops[loop].difference(objs)
561                     if t.sname in diff:
562                         diff.remove(t.sname)
563                     if diff:
564                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
565                         objs = objs.union(diff)
566                 setattr(t, attr, objs)
567
568         for loop in inc_loops:
569             objs = getattr(t, 'includes_objects', set())
570             if loop in objs:
571                 diff = inc_loops[loop].difference(objs)
572                 if t.sname in diff:
573                     diff.remove(t.sname)
574                 if diff:
575                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
576                     objs = objs.union(diff)
577             setattr(t, 'includes_objects', objs)
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_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
584         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
585
586     for t in tgt_list:
587         # don't depend on ourselves
588         if t.sname in t.final_libs:
589             t.final_libs.remove(t.sname)
590         if t.sname in t.final_objects:
591             t.final_objects.remove(t.sname)
592
593     # find any library loops
594     for t in tgt_list:
595         if t.samba_type in ['LIBRARY', 'PYTHON']:
596             for l in t.final_libs.copy():
597                 t2 = bld.name_to_obj(l, bld.env)
598                 if t.sname in t2.final_libs:
599                     # we could break this in either direction. If one of the libraries
600                     # has a version number, and will this be distributed publicly, then
601                     # we should make it the lower level library in the DAG
602                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
603                     dependency_loop(loops, t, t2.sname)
604                     t2.final_libs.remove(t.sname)
605
606     for type in ['BINARY', 'PYTHON']:
607         for t in tgt_list:
608             if t.samba_type != type: continue
609             # if we will indirectly link to a target then we don't need it
610             new = t.final_objects.copy()
611             for l in t.final_libs:
612                 t2 = bld.name_to_obj(l, bld.env)
613                 t2_obj = extended_objects(bld, t2, set())
614                 dup = new.intersection(t2_obj)
615                 if dup:
616                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
617                           t.sname, t.samba_type, dup, t2.samba_type, l)
618                     new = new.difference(dup)
619                     changed = True
620             t.final_objects = new
621
622     for loop in loops:
623         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
624
625     # we now need to make corrections for any library loops we broke up
626     # any target that depended on the target of the loop and doesn't
627     # depend on the source of the loop needs to get the loop source added
628     for type in ['BINARY','PYTHON','LIBRARY']:
629         for t in tgt_list:
630             if t.samba_type != type: continue
631             for loop in loops:
632                 if loop in t.final_libs:
633                     diff = loops[loop].difference(t.final_libs)
634                     if t.sname in diff:
635                         diff.remove(t.sname)
636                     if diff:
637                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
638                         t.final_libs = t.final_libs.union(diff)
639
640     # add in any syslib dependencies
641     for t in tgt_list:
642         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
643             continue
644         syslibs = set()
645         for d in t.final_objects:
646             t2 = bld.name_to_obj(d, bld.env)
647             syslibs = syslibs.union(t2.direct_syslibs)
648         # this adds the indirect syslibs as well, which may not be needed
649         # depending on the linker flags
650         for d in t.final_libs:
651             t2 = bld.name_to_obj(d, bld.env)
652             syslibs = syslibs.union(t2.direct_syslibs)
653         t.final_syslibs = syslibs
654
655     debug('deps: removed duplicate dependencies')
656
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', 'SYSLIB_DEPS']
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