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