s4-waf: added implied_deps for system libraries
[nivanova/samba-autobuild/.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, re, Environment
4 from samba_utils import *
5 from samba_autoconf import *
6
7 @conf
8 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
9     '''add a dependency for all binaries and libraries'''
10     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
11         ctx.env.GLOBAL_DEPENDENCIES = []
12     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
13
14
15 def TARGET_ALIAS(bld, target, alias):
16     '''define an alias for a target name'''
17     cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
18     if alias in cache:
19         print("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
20         raise
21     cache[alias] = target
22 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
23
24
25 @conf
26 def SET_SYSLIB_DEPS(conf, target, deps):
27     '''setup some implied dependencies for a SYSLIB'''
28     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
29     cache[target] = deps
30
31
32 def EXPAND_ALIAS(bld, target):
33     '''expand a target name via an alias'''
34     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
35     if target in aliases:
36         return aliases[target]
37     return target
38 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
39
40
41 def expand_subsystem_deps(bld):
42     '''expand the reverse dependencies resulting from subsystem
43        attributes of modules'''
44     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
45     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
46     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
47
48     for s in subsystems:
49         if s in aliases:
50             s = aliases[s]
51         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
52         type = targets[s]
53         if type == 'DISABLED' or type == 'EMPTY':
54             continue
55
56         t = bld.name_to_obj(s, bld.env)
57         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
58         for d in subsystems[s]:
59             type = targets[d['TARGET']]
60             if type != 'DISABLED' and type != 'EMPTY':
61                 t.samba_deps_extended.append(d['TARGET'])
62                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
63                 t2.samba_includes_extended.extend(t.samba_includes_extended)
64                 t2.samba_deps_extended.extend(t.samba_deps_extended)
65         t.samba_deps_extended = unique_list(t.samba_deps_extended)
66
67
68
69 def build_dependencies(self):
70     '''This builds the dependency list for a target. It runs after all the targets are declared
71
72     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
73     the full dependency list for a target until we have all of the targets declared.
74     '''
75
76     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
77         self.uselib        = list(self.final_syslibs)
78         self.uselib_local  = list(self.final_libs)
79         self.add_objects   = list(self.final_objects)
80
81         # extra link flags from pkg_config
82         libs = self.final_syslibs.copy()
83
84         (ccflags, ldflags) = library_flags(self, list(libs))
85         new_ldflags        = getattr(self, 'ldflags', [])
86         new_ldflags.extend(ldflags)
87         self.ldflags       = new_ldflags
88
89         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
90               self.sname, self.uselib, self.uselib_local, self.add_objects)
91
92     if self.samba_type in ['SUBSYSTEM']:
93         # this is needed for the ccflags of libs that come from pkg_config
94         self.uselib = list(self.direct_syslibs)
95
96     if getattr(self, 'uselib', None):
97         up_list = []
98         for l in self.uselib:
99             up_list.append(l.upper())
100         self.uselib = up_list
101
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                     print("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                 print "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                 print "Unknown dependency %s in %s" % (d, t.sname)
332                 raise
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                         print("Adding implied lib %s to %s" % (implied, t.sname))
340                         t.direct_libs.add(implied)
341                 continue
342             t2 = bld.name_to_obj(d, bld.env)
343             if t2 is None:
344                 print "no task %s type %s" % (d, targets[d])
345             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
346                 t.direct_libs.add(d)
347             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
348                 t.direct_objects.add(d)
349     debug('deps: built direct dependencies')
350
351
352 def dependency_loop(loops, t, target):
353     '''add a dependency loop to the loops dictionary'''
354     if t.sname == target:
355         return
356     if not target in loops:
357         loops[target] = set()
358     if not t.sname in loops[target]:
359         loops[target].add(t.sname)
360
361
362 def indirect_libs(bld, t, chain, loops):
363     '''recursively calculate the indirect library dependencies for a target
364
365     An indirect library is a library that results from a dependency on
366     a subsystem
367     '''
368
369     ret = getattr(t, 'indirect_libs', None)
370     if ret is not None:
371         return ret
372
373     ret = set()
374     for obj in t.direct_objects:
375         if obj in chain:
376             dependency_loop(loops, t, obj)
377             continue
378         chain.add(obj)
379         t2 = bld.name_to_obj(obj, bld.env)
380         r2 = indirect_libs(bld, t2, chain, loops)
381         chain.remove(obj)
382         ret = ret.union(t2.direct_libs)
383         ret = ret.union(r2)
384
385     for obj in indirect_objects(bld, t, set(), loops):
386         if obj in chain:
387             dependency_loop(loops, t, obj)
388             continue
389         chain.add(obj)
390         t2 = bld.name_to_obj(obj, bld.env)
391         r2 = indirect_libs(bld, t2, chain, loops)
392         chain.remove(obj)
393         ret = ret.union(t2.direct_libs)
394         ret = ret.union(r2)
395
396     t.indirect_libs = ret
397
398     return ret
399
400
401 def indirect_objects(bld, t, chain, loops):
402     '''recursively calculate the indirect object dependencies for a target
403
404     indirect objects are the set of objects from expanding the
405     subsystem dependencies
406     '''
407
408     ret = getattr(t, 'indirect_objects', None)
409     if ret is not None: return ret
410
411     ret = set()
412     for lib in t.direct_objects:
413         if lib in chain:
414             dependency_loop(loops, t, lib)
415             continue
416         chain.add(lib)
417         t2 = bld.name_to_obj(lib, bld.env)
418         r2 = indirect_objects(bld, t2, chain, loops)
419         chain.remove(lib)
420         ret = ret.union(t2.direct_objects)
421         ret = ret.union(r2)
422
423     t.indirect_objects = ret
424     return ret
425
426
427 def extended_objects(bld, t, chain):
428     '''recursively calculate the extended object dependencies for a target
429
430     extended objects are the union of:
431        - direct objects
432        - indirect objects
433        - direct and indirect objects of all direct and indirect libraries
434     '''
435
436     ret = getattr(t, 'extended_objects', None)
437     if ret is not None: return ret
438
439     ret = set()
440     ret = ret.union(t.direct_objects)
441     ret = ret.union(t.indirect_objects)
442
443     for lib in t.direct_libs:
444         if lib in chain:
445             continue
446         t2 = bld.name_to_obj(lib, bld.env)
447         chain.add(lib)
448         r2 = extended_objects(bld, t2, chain)
449         chain.remove(lib)
450         ret = ret.union(t2.direct_objects)
451         ret = ret.union(t2.indirect_objects)
452         ret = ret.union(r2)
453
454     t.extended_objects = ret
455     return ret
456
457
458 def includes_objects(bld, t, chain, inc_loops):
459     '''recursively calculate the includes object dependencies for a target
460
461     includes dependencies come from either library or object dependencies
462     '''
463     ret = getattr(t, 'includes_objects', None)
464     if ret is not None:
465         return ret
466
467     ret = t.direct_objects.copy()
468     ret = ret.union(t.direct_libs)
469
470     for obj in t.direct_objects:
471         if obj in chain:
472             dependency_loop(inc_loops, t, obj)
473             continue
474         chain.add(obj)
475         t2 = bld.name_to_obj(obj, bld.env)
476         r2 = includes_objects(bld, t2, chain, inc_loops)
477         chain.remove(obj)
478         ret = ret.union(t2.direct_objects)
479         ret = ret.union(r2)
480
481     for lib in t.direct_libs:
482         if lib in chain:
483             dependency_loop(inc_loops, t, lib)
484             continue
485         chain.add(lib)
486         t2 = bld.name_to_obj(lib, bld.env)
487         r2 = includes_objects(bld, t2, chain, inc_loops)
488         chain.remove(lib)
489         ret = ret.union(t2.direct_objects)
490         ret = ret.union(r2)
491
492     t.includes_objects = ret
493     return ret
494
495
496 def break_dependency_loops(bld, tgt_list):
497     '''find and break dependency loops'''
498     loops = {}
499     inc_loops = {}
500
501     # build up the list of loops
502     for t in tgt_list:
503         indirect_objects(bld, t, set(), loops)
504         indirect_libs(bld, t, set(), loops)
505         includes_objects(bld, t, set(), inc_loops)
506
507     # break the loops
508     for t in tgt_list:
509         if t.sname in loops:
510             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
511                 objs = getattr(t, attr, set())
512                 setattr(t, attr, objs.difference(loops[t.sname]))
513
514     for loop in loops:
515         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
516
517     # expand the loops mapping by one level
518     for loop in loops.copy():
519         for tgt in loops[loop]:
520             if tgt in loops:
521                 loops[loop] = loops[loop].union(loops[tgt])
522
523     # expand indirect subsystem and library loops
524     for loop in loops.copy():
525         t = bld.name_to_obj(loop, bld.env)
526         if t.samba_type in ['SUBSYSTEM']:
527             loops[loop] = loops[loop].union(t.indirect_objects)
528             loops[loop] = loops[loop].union(t.direct_objects)
529         if t.samba_type in ['LIBRARY','PYTHON']:
530             loops[loop] = loops[loop].union(t.indirect_libs)
531             loops[loop] = loops[loop].union(t.direct_libs)
532         if loop in loops[loop]:
533             loops[loop].remove(loop)
534
535     # add in the replacement dependencies
536     for t in tgt_list:
537         for loop in loops:
538             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
539                 objs = getattr(t, attr, set())
540                 if loop in objs:
541                     diff = loops[loop].difference(objs)
542                     if t.sname in diff:
543                         diff.remove(t.sname)
544                     if diff:
545                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
546                         objs = objs.union(diff)
547                 if t.sname == 'ldb_password_hash':
548                     debug('deps: setting %s %s to %s', t.sname, attr, objs)
549                 setattr(t, attr, objs)
550
551 def calculate_final_deps(bld, tgt_list, loops):
552     '''calculate the final library and object dependencies'''
553     for t in tgt_list:
554         # start with the maximum possible list
555         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
556         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
557
558     for t in tgt_list:
559         # don't depend on ourselves
560         if t.sname in t.final_libs:
561             t.final_libs.remove(t.sname)
562         if t.sname in t.final_objects:
563             t.final_objects.remove(t.sname)
564
565     # find any library loops
566     for t in tgt_list:
567         if t.samba_type in ['LIBRARY', 'PYTHON']:
568             for l in t.final_libs.copy():
569                 t2 = bld.name_to_obj(l, bld.env)
570                 if t.sname in t2.final_libs:
571                     # we could break this in either direction. If one of the libraries
572                     # has a version number, and will this be distributed publicly, then
573                     # we should make it the lower level library in the DAG
574                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
575                     dependency_loop(loops, t, t2.sname)
576                     t2.final_libs.remove(t.sname)
577
578     for type in ['BINARY']:
579         for t in tgt_list:
580             if t.samba_type != type: continue
581             # if we will indirectly link to a target then we don't need it
582             new = t.final_objects.copy()
583             for l in t.final_libs:
584                 t2 = bld.name_to_obj(l, bld.env)
585                 t2_obj = extended_objects(bld, t2, set())
586                 dup = new.intersection(t2_obj)
587                 if dup:
588                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
589                           t.sname, t.samba_type, dup, t2.samba_type, l)
590                     new = new.difference(dup)
591                     changed = True
592             t.final_objects = new
593
594     for loop in loops:
595         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
596
597     # we now need to make corrections for any library loops we broke up
598     # any target that depended on the target of the loop and doesn't
599     # depend on the source of the loop needs to get the loop source added
600     for type in ['BINARY','PYTHON','LIBRARY']:
601         for t in tgt_list:
602             if t.samba_type != type: continue
603             for loop in loops:
604                 if loop in t.final_libs:
605                     diff = loops[loop].difference(t.final_libs)
606                     if t.sname in diff:
607                         diff.remove(t.sname)
608                     if diff:
609                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
610                         t.final_libs = t.final_libs.union(diff)
611
612     # add in any syslib dependencies
613     for t in tgt_list:
614         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
615             continue
616         syslibs = set()
617         for d in t.final_objects:
618             t2 = bld.name_to_obj(d, bld.env)
619             syslibs = syslibs.union(t2.direct_syslibs)
620         # this adds the indirect syslibs as well, which may not be needed
621         # depending on the linker flags
622         for d in t.final_libs:
623             t2 = bld.name_to_obj(d, bld.env)
624             syslibs = syslibs.union(t2.direct_syslibs)
625         t.final_syslibs = syslibs
626
627     debug('deps: removed duplicate dependencies')
628
629
630
631 ######################################################################
632 # this provides a way to save our dependency calculations between runs
633 savedeps_version = 3
634 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
635 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
636 savedeps_outenv  = ['INC_PATHS']
637 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
638 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
639
640 def save_samba_deps(bld, tgt_list):
641     '''save the dependency calculations between builds, to make
642        further builds faster'''
643     denv = Environment.Environment()
644
645     denv.version = savedeps_version
646     denv.savedeps_inputs = savedeps_inputs
647     denv.savedeps_outputs = savedeps_outputs
648     denv.input = {}
649     denv.output = {}
650     denv.outenv = {}
651     denv.caches = {}
652     denv.files  = {}
653
654     for f in savedeps_files:
655         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
656
657     for c in savedeps_caches:
658         denv.caches[c] = LOCAL_CACHE(bld, c)
659
660     for t in tgt_list:
661         # save all the input attributes for each target
662         tdeps = {}
663         for attr in savedeps_inputs:
664             v = getattr(t, attr, None)
665             if v is not None:
666                 tdeps[attr] = v
667         if tdeps != {}:
668             denv.input[t.sname] = tdeps
669
670         # save all the output attributes for each target
671         tdeps = {}
672         for attr in savedeps_outputs:
673             v = getattr(t, attr, None)
674             if v is not None:
675                 tdeps[attr] = v
676         if tdeps != {}:
677             denv.output[t.sname] = tdeps
678
679         tdeps = {}
680         for attr in savedeps_outenv:
681             if attr in t.env:
682                 tdeps[attr] = t.env[attr]
683         if tdeps != {}:
684             denv.outenv[t.sname] = tdeps
685
686     depsfile = os.path.join(bld.bdir, "sambadeps")
687     denv.store(depsfile)
688
689
690 def load_samba_deps(bld, tgt_list):
691     '''load a previous set of build dependencies if possible'''
692     depsfile = os.path.join(bld.bdir, "sambadeps")
693     denv = Environment.Environment()
694     try:
695         debug('deps: checking saved dependencies')
696         denv.load(depsfile)
697         if (denv.version != savedeps_version or
698             denv.savedeps_inputs != savedeps_inputs or
699             denv.savedeps_outputs != savedeps_outputs):
700             return False
701     except:
702         return False
703
704     # check if critical files have changed
705     for f in savedeps_files:
706         if f not in denv.files:
707             return False
708         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
709             return False
710
711     # check if caches are the same
712     for c in savedeps_caches:
713         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
714             return False
715
716     # check inputs are the same
717     for t in tgt_list:
718         tdeps = {}
719         for attr in savedeps_inputs:
720             v = getattr(t, attr, None)
721             if v is not None:
722                 tdeps[attr] = v
723         if t.sname in denv.input:
724             olddeps = denv.input[t.sname]
725         else:
726             olddeps = {}
727         if tdeps != olddeps:
728             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
729             return False
730
731     # put outputs in place
732     for t in tgt_list:
733         if not t.sname in denv.output: continue
734         tdeps = denv.output[t.sname]
735         for a in tdeps:
736             setattr(t, a, tdeps[a])
737
738     # put output env vars in place
739     for t in tgt_list:
740         if not t.sname in denv.outenv: continue
741         tdeps = denv.outenv[t.sname]
742         for a in tdeps:
743             t.env[a] = tdeps[a]
744
745     debug('deps: loaded saved dependencies')
746     return True
747
748
749 def check_project_rules(bld):
750     '''check the project rules - ensuring the targets are sane'''
751
752     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
753     loops = {}
754     inc_loops = {}
755
756     # build a list of task generators we are interested in
757     tgt_list = []
758     for tgt in targets:
759         type = targets[tgt]
760         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
761             continue
762         t = bld.name_to_obj(tgt, bld.env)
763         if t is None:
764             print "Target %s of type %s has no task generator" % (tgt, type)
765             raise
766         tgt_list.append(t)
767
768     add_samba_attributes(bld, tgt_list)
769
770     if load_samba_deps(bld, tgt_list):
771         return
772
773     print "Checking project rules ..."
774
775     debug('deps: project rules checking started')
776
777     expand_subsystem_deps(bld)
778     build_direct_deps(bld, tgt_list)
779     break_dependency_loops(bld, tgt_list)
780     calculate_final_deps(bld, tgt_list, loops)
781
782     # run the various attribute generators
783     for f in [ build_dependencies, build_includes, add_init_functions ]:
784         debug('deps: project rules checking %s', f)
785         for t in tgt_list: f(t)
786
787     debug('deps: project rules stage1 completed')
788
789     #check_orpaned_targets(bld, tgt_list)
790
791     if not check_duplicate_sources(bld, tgt_list):
792         print "Duplicate sources present - aborting"
793         sys.exit(1)
794
795     show_final_deps(bld, tgt_list)
796
797     debug('deps: project rules checking completed - %u targets checked',
798           len(tgt_list))
799
800     save_samba_deps(bld, tgt_list)
801
802     print "Project rules pass"
803
804
805 def CHECK_PROJECT_RULES(bld):
806     '''enable checking of project targets for sanity'''
807     if bld.env.added_project_rules:
808         return
809     bld.env.added_project_rules = True
810     bld.add_pre_fun(check_project_rules)
811 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
812
813