build: fixed the dependencies of the install targets
[sfrench/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         install_target = getattr(t, 'install_target', None)
314         if install_target:
315             t2 = bld.name_to_obj(install_target, bld.env)
316             t2.sname = install_target
317             t2.samba_type = t.samba_type
318             t2.samba_abspath = t2.path.abspath(bld.env)
319             t2.ccflags = t.ccflags
320
321
322 def build_direct_deps(bld, tgt_list):
323     '''build the direct_objects and direct_libs sets for each target'''
324
325     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
326     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
327     global_deps = bld.env.GLOBAL_DEPENDENCIES
328
329     for t in tgt_list:
330         t.direct_objects = set()
331         t.direct_libs = set()
332         t.direct_syslibs = set()
333         deps = t.samba_deps_extended
334         deps.extend(global_deps)
335         for d in deps:
336             d = EXPAND_ALIAS(bld, d)
337             if d == t.sname: continue
338             if not d in targets:
339                 print "Unknown dependency %s in %s" % (d, t.sname)
340                 raise
341             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
342                 continue
343             if targets[d] == 'SYSLIB':
344                 t.direct_syslibs.add(d)
345                 if d in syslib_deps:
346                     for implied in TO_LIST(syslib_deps[d]):
347                         if BUILTIN_LIBRARY(bld, implied):
348                             t.direct_objects.add(implied)
349                         else:
350                             t.direct_libs.add(implied)
351                 continue
352             t2 = bld.name_to_obj(d, bld.env)
353             if t2 is None:
354                 print "no task %s type %s" % (d, targets[d])
355             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
356                 t.direct_libs.add(d)
357             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
358                 t.direct_objects.add(d)
359     debug('deps: built direct dependencies')
360
361
362 def dependency_loop(loops, t, target):
363     '''add a dependency loop to the loops dictionary'''
364     if t.sname == target:
365         return
366     if not target in loops:
367         loops[target] = set()
368     if not t.sname in loops[target]:
369         loops[target].add(t.sname)
370
371
372 def indirect_libs(bld, t, chain, loops):
373     '''recursively calculate the indirect library dependencies for a target
374
375     An indirect library is a library that results from a dependency on
376     a subsystem
377     '''
378
379     ret = getattr(t, 'indirect_libs', None)
380     if ret is not None:
381         return ret
382
383     ret = set()
384     for obj in t.direct_objects:
385         if obj in chain:
386             dependency_loop(loops, t, obj)
387             continue
388         chain.add(obj)
389         t2 = bld.name_to_obj(obj, bld.env)
390         r2 = indirect_libs(bld, t2, chain, loops)
391         chain.remove(obj)
392         ret = ret.union(t2.direct_libs)
393         ret = ret.union(r2)
394
395     for obj in indirect_objects(bld, t, set(), loops):
396         if obj in chain:
397             dependency_loop(loops, t, obj)
398             continue
399         chain.add(obj)
400         t2 = bld.name_to_obj(obj, bld.env)
401         r2 = indirect_libs(bld, t2, chain, loops)
402         chain.remove(obj)
403         ret = ret.union(t2.direct_libs)
404         ret = ret.union(r2)
405
406     t.indirect_libs = ret
407
408     return ret
409
410
411 def indirect_objects(bld, t, chain, loops):
412     '''recursively calculate the indirect object dependencies for a target
413
414     indirect objects are the set of objects from expanding the
415     subsystem dependencies
416     '''
417
418     ret = getattr(t, 'indirect_objects', None)
419     if ret is not None: return ret
420
421     ret = set()
422     for lib in t.direct_objects:
423         if lib in chain:
424             dependency_loop(loops, t, lib)
425             continue
426         chain.add(lib)
427         t2 = bld.name_to_obj(lib, bld.env)
428         r2 = indirect_objects(bld, t2, chain, loops)
429         chain.remove(lib)
430         ret = ret.union(t2.direct_objects)
431         ret = ret.union(r2)
432
433     t.indirect_objects = ret
434     return ret
435
436
437 def extended_objects(bld, t, chain):
438     '''recursively calculate the extended object dependencies for a target
439
440     extended objects are the union of:
441        - direct objects
442        - indirect objects
443        - direct and indirect objects of all direct and indirect libraries
444     '''
445
446     ret = getattr(t, 'extended_objects', None)
447     if ret is not None: return ret
448
449     ret = set()
450     ret = ret.union(t.final_objects)
451
452     for lib in t.final_libs:
453         if lib in chain:
454             continue
455         t2 = bld.name_to_obj(lib, bld.env)
456         chain.add(lib)
457         r2 = extended_objects(bld, t2, chain)
458         chain.remove(lib)
459         ret = ret.union(t2.final_objects)
460         ret = ret.union(r2)
461
462     t.extended_objects = ret
463     return ret
464
465
466 def includes_objects(bld, t, chain, inc_loops):
467     '''recursively calculate the includes object dependencies for a target
468
469     includes dependencies come from either library or object dependencies
470     '''
471     ret = getattr(t, 'includes_objects', None)
472     if ret is not None:
473         return ret
474
475     ret = t.direct_objects.copy()
476     ret = ret.union(t.direct_libs)
477
478     for obj in t.direct_objects:
479         if obj in chain:
480             dependency_loop(inc_loops, t, obj)
481             continue
482         chain.add(obj)
483         t2 = bld.name_to_obj(obj, bld.env)
484         r2 = includes_objects(bld, t2, chain, inc_loops)
485         chain.remove(obj)
486         ret = ret.union(t2.direct_objects)
487         ret = ret.union(r2)
488
489     for lib in t.direct_libs:
490         if lib in chain:
491             dependency_loop(inc_loops, t, lib)
492             continue
493         chain.add(lib)
494         t2 = bld.name_to_obj(lib, bld.env)
495         r2 = includes_objects(bld, t2, chain, inc_loops)
496         chain.remove(lib)
497         ret = ret.union(t2.direct_objects)
498         ret = ret.union(r2)
499
500     t.includes_objects = ret
501     return ret
502
503
504 def break_dependency_loops(bld, tgt_list):
505     '''find and break dependency loops'''
506     loops = {}
507     inc_loops = {}
508
509     # build up the list of loops
510     for t in tgt_list:
511         indirect_objects(bld, t, set(), loops)
512         indirect_libs(bld, t, set(), loops)
513         includes_objects(bld, t, set(), inc_loops)
514
515     # break the loops
516     for t in tgt_list:
517         if t.sname in loops:
518             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
519                 objs = getattr(t, attr, set())
520                 setattr(t, attr, objs.difference(loops[t.sname]))
521
522     for loop in loops:
523         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
524
525     for loop in inc_loops:
526         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
527
528     # expand the loops mapping by one level
529     for loop in loops.copy():
530         for tgt in loops[loop]:
531             if tgt in loops:
532                 loops[loop] = loops[loop].union(loops[tgt])
533
534     for loop in inc_loops.copy():
535         for tgt in inc_loops[loop]:
536             if tgt in inc_loops:
537                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
538
539
540     # expand indirect subsystem and library loops
541     for loop in loops.copy():
542         t = bld.name_to_obj(loop, bld.env)
543         if t.samba_type in ['SUBSYSTEM']:
544             loops[loop] = loops[loop].union(t.indirect_objects)
545             loops[loop] = loops[loop].union(t.direct_objects)
546         if t.samba_type in ['LIBRARY','PYTHON']:
547             loops[loop] = loops[loop].union(t.indirect_libs)
548             loops[loop] = loops[loop].union(t.direct_libs)
549         if loop in loops[loop]:
550             loops[loop].remove(loop)
551
552     # expand indirect includes loops
553     for loop in inc_loops.copy():
554         t = bld.name_to_obj(loop, bld.env)
555         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
556         if loop in inc_loops[loop]:
557             inc_loops[loop].remove(loop)
558
559     # add in the replacement dependencies
560     for t in tgt_list:
561         for loop in loops:
562             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
563                 objs = getattr(t, attr, set())
564                 if loop in objs:
565                     diff = loops[loop].difference(objs)
566                     if t.sname in diff:
567                         diff.remove(t.sname)
568                     if diff:
569                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
570                         objs = objs.union(diff)
571                 setattr(t, attr, objs)
572
573         for loop in inc_loops:
574             objs = getattr(t, 'includes_objects', set())
575             if loop in objs:
576                 diff = inc_loops[loop].difference(objs)
577                 if t.sname in diff:
578                     diff.remove(t.sname)
579                 if diff:
580                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
581                     objs = objs.union(diff)
582             setattr(t, 'includes_objects', objs)
583
584
585 def reduce_objects(bld, tgt_list):
586     '''reduce objects by looking for indirect object dependencies'''
587     rely_on = {}
588
589     for t in tgt_list:
590         t.extended_objects = None
591
592     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
593         for t in tgt_list:
594             if t.samba_type != type: continue
595             # if we will indirectly link to a target then we don't need it
596             new = t.final_objects.copy()
597             for l in t.final_libs:
598                 t2 = bld.name_to_obj(l, bld.env)
599                 t2_obj = extended_objects(bld, t2, set())
600                 dup = new.intersection(t2_obj)
601                 if dup:
602                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
603                           t.sname, t.samba_type, dup, t2.samba_type, l)
604                     new = new.difference(dup)
605                     changed = True
606                     if not l in rely_on:
607                         rely_on[l] = set()
608                     rely_on[l] = rely_on[l].union(dup)
609             t.final_objects = new
610
611     # add back in any objects that were relied upon by the reduction rules
612     for r in rely_on:
613         t = bld.name_to_obj(r, bld.env)
614         t.final_objects = t.final_objects.union(rely_on[r])
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     reduce_objects(bld, tgt_list)
666
667     # add in any syslib dependencies
668     for t in tgt_list:
669         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
670             continue
671         syslibs = set()
672         for d in t.final_objects:
673             t2 = bld.name_to_obj(d, bld.env)
674             syslibs = syslibs.union(t2.direct_syslibs)
675         # this adds the indirect syslibs as well, which may not be needed
676         # depending on the linker flags
677         for d in t.final_libs:
678             t2 = bld.name_to_obj(d, bld.env)
679             syslibs = syslibs.union(t2.direct_syslibs)
680         t.final_syslibs = syslibs
681
682     debug('deps: removed duplicate dependencies')
683
684
685 ######################################################################
686 # this provides a way to save our dependency calculations between runs
687 savedeps_version = 3
688 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
689 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
690 savedeps_outenv  = ['INC_PATHS']
691 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
692 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
693
694 def save_samba_deps(bld, tgt_list):
695     '''save the dependency calculations between builds, to make
696        further builds faster'''
697     denv = Environment.Environment()
698
699     denv.version = savedeps_version
700     denv.savedeps_inputs = savedeps_inputs
701     denv.savedeps_outputs = savedeps_outputs
702     denv.input = {}
703     denv.output = {}
704     denv.outenv = {}
705     denv.caches = {}
706     denv.files  = {}
707
708     for f in savedeps_files:
709         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
710
711     for c in savedeps_caches:
712         denv.caches[c] = LOCAL_CACHE(bld, c)
713
714     for t in tgt_list:
715         # save all the input attributes for each target
716         tdeps = {}
717         for attr in savedeps_inputs:
718             v = getattr(t, attr, None)
719             if v is not None:
720                 tdeps[attr] = v
721         if tdeps != {}:
722             denv.input[t.sname] = tdeps
723
724         # save all the output attributes for each target
725         tdeps = {}
726         for attr in savedeps_outputs:
727             v = getattr(t, attr, None)
728             if v is not None:
729                 tdeps[attr] = v
730         if tdeps != {}:
731             denv.output[t.sname] = tdeps
732
733         tdeps = {}
734         for attr in savedeps_outenv:
735             if attr in t.env:
736                 tdeps[attr] = t.env[attr]
737         if tdeps != {}:
738             denv.outenv[t.sname] = tdeps
739
740     depsfile = os.path.join(bld.bdir, "sambadeps")
741     denv.store(depsfile)
742
743
744
745 def load_samba_deps(bld, tgt_list):
746     '''load a previous set of build dependencies if possible'''
747     depsfile = os.path.join(bld.bdir, "sambadeps")
748     denv = Environment.Environment()
749     try:
750         debug('deps: checking saved dependencies')
751         denv.load(depsfile)
752         if (denv.version != savedeps_version or
753             denv.savedeps_inputs != savedeps_inputs or
754             denv.savedeps_outputs != savedeps_outputs):
755             return False
756     except:
757         return False
758
759     # check if critical files have changed
760     for f in savedeps_files:
761         if f not in denv.files:
762             return False
763         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
764             return False
765
766     # check if caches are the same
767     for c in savedeps_caches:
768         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
769             return False
770
771     # check inputs are the same
772     for t in tgt_list:
773         tdeps = {}
774         for attr in savedeps_inputs:
775             v = getattr(t, attr, None)
776             if v is not None:
777                 tdeps[attr] = v
778         if t.sname in denv.input:
779             olddeps = denv.input[t.sname]
780         else:
781             olddeps = {}
782         if tdeps != olddeps:
783             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
784             return False
785
786     tgt_list_extended = tgt_list[:]
787     for t in tgt_list:
788         install_target = getattr(t, 'install_target', None)
789         if install_target:
790             t2 = bld.name_to_obj(install_target, bld.env)
791             tgt_list_extended.append(t2)
792
793     # put outputs in place
794     for t in tgt_list_extended:
795         if not t.sname in denv.output: continue
796         tdeps = denv.output[t.sname]
797         for a in tdeps:
798             setattr(t, a, tdeps[a])
799
800     # put output env vars in place
801     for t in tgt_list_extended:
802         if not t.sname in denv.outenv: continue
803         tdeps = denv.outenv[t.sname]
804         for a in tdeps:
805             t.env[a] = tdeps[a]
806
807     debug('deps: loaded saved dependencies')
808     return True
809
810
811
812 def add_install_deps(bld, tgt_list):
813     '''add attributes for install libs/binaries
814
815     This ensures that all the install targets have identical dependencies
816     to the build targets.
817     '''
818     for t in tgt_list[:]:
819         install_target = getattr(t, 'install_target', None)
820         if install_target:
821             t2 = bld.name_to_obj(install_target, bld.env)
822             if not t2:
823                 print('install_target %s not found for %s' % (install_target, t.sname))
824                 sys.exit(1)
825             tgt_list.append(t2)
826             for attr in savedeps_outputs:
827                 v = getattr(t, attr, None)
828                 if v:
829                     setattr(t2, attr, v)
830             for attr in savedeps_outenv:
831                 if attr in t.env:
832                     t2.env[attr] = t.env[attr]
833
834
835 def check_project_rules(bld):
836     '''check the project rules - ensuring the targets are sane'''
837
838     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
839     loops = {}
840     inc_loops = {}
841
842     # build a list of task generators we are interested in
843     tgt_list = []
844     for tgt in targets:
845         if tgt.endswith('.inst'):
846             continue
847         type = targets[tgt]
848         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
849             continue
850         t = bld.name_to_obj(tgt, bld.env)
851         if t is None:
852             print "Target %s of type %s has no task generator" % (tgt, type)
853             raise
854         tgt_list.append(t)
855
856     add_samba_attributes(bld, tgt_list)
857
858     if load_samba_deps(bld, tgt_list):
859         add_install_deps(bld, tgt_list)
860         return
861
862     print "Checking project rules ..."
863
864     debug('deps: project rules checking started')
865
866     expand_subsystem_deps(bld)
867     build_direct_deps(bld, tgt_list)
868     break_dependency_loops(bld, tgt_list)
869     calculate_final_deps(bld, tgt_list, loops)
870
871     # run the various attribute generators
872     for f in [ build_dependencies, build_includes, add_init_functions ]:
873         debug('deps: project rules checking %s', f)
874         for t in tgt_list: f(t)
875
876     debug('deps: project rules stage1 completed')
877
878     #check_orpaned_targets(bld, tgt_list)
879
880     if not check_duplicate_sources(bld, tgt_list):
881         print "Duplicate sources present - aborting"
882         sys.exit(1)
883
884     show_final_deps(bld, tgt_list)
885
886     debug('deps: project rules checking completed - %u targets checked',
887           len(tgt_list))
888
889     save_samba_deps(bld, tgt_list)
890
891     add_install_deps(bld, tgt_list)
892
893     print "Project rules pass"
894
895
896 def CHECK_PROJECT_RULES(bld):
897     '''enable checking of project targets for sanity'''
898     if bld.env.added_project_rules:
899         return
900     bld.env.added_project_rules = True
901     bld.add_pre_fun(check_project_rules)
902 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
903
904