build: much simpler and faster rpath install handler
[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
314
315 def build_direct_deps(bld, tgt_list):
316     '''build the direct_objects and direct_libs sets for each target'''
317
318     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
319     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
320     global_deps = bld.env.GLOBAL_DEPENDENCIES
321
322     for t in tgt_list:
323         t.direct_objects = set()
324         t.direct_libs = set()
325         t.direct_syslibs = set()
326         deps = t.samba_deps_extended
327         deps.extend(global_deps)
328         for d in deps:
329             d = EXPAND_ALIAS(bld, d)
330             if d == t.sname: continue
331             if not d in targets:
332                 print "Unknown dependency %s in %s" % (d, t.sname)
333                 raise
334             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
335                 continue
336             if targets[d] == 'SYSLIB':
337                 t.direct_syslibs.add(d)
338                 if d in syslib_deps:
339                     for implied in TO_LIST(syslib_deps[d]):
340                         if BUILTIN_LIBRARY(bld, implied):
341                             t.direct_objects.add(implied)
342                         else:
343                             t.direct_libs.add(implied)
344                 continue
345             t2 = bld.name_to_obj(d, bld.env)
346             if t2 is None:
347                 print "no task %s type %s" % (d, targets[d])
348             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
349                 t.direct_libs.add(d)
350             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
351                 t.direct_objects.add(d)
352     debug('deps: built direct dependencies')
353
354
355 def dependency_loop(loops, t, target):
356     '''add a dependency loop to the loops dictionary'''
357     if t.sname == target:
358         return
359     if not target in loops:
360         loops[target] = set()
361     if not t.sname in loops[target]:
362         loops[target].add(t.sname)
363
364
365 def indirect_libs(bld, t, chain, loops):
366     '''recursively calculate the indirect library dependencies for a target
367
368     An indirect library is a library that results from a dependency on
369     a subsystem
370     '''
371
372     ret = getattr(t, 'indirect_libs', None)
373     if ret is not None:
374         return ret
375
376     ret = set()
377     for obj in t.direct_objects:
378         if obj in chain:
379             dependency_loop(loops, t, obj)
380             continue
381         chain.add(obj)
382         t2 = bld.name_to_obj(obj, bld.env)
383         r2 = indirect_libs(bld, t2, chain, loops)
384         chain.remove(obj)
385         ret = ret.union(t2.direct_libs)
386         ret = ret.union(r2)
387
388     for obj in indirect_objects(bld, t, set(), loops):
389         if obj in chain:
390             dependency_loop(loops, t, obj)
391             continue
392         chain.add(obj)
393         t2 = bld.name_to_obj(obj, bld.env)
394         r2 = indirect_libs(bld, t2, chain, loops)
395         chain.remove(obj)
396         ret = ret.union(t2.direct_libs)
397         ret = ret.union(r2)
398
399     t.indirect_libs = ret
400
401     return ret
402
403
404 def indirect_objects(bld, t, chain, loops):
405     '''recursively calculate the indirect object dependencies for a target
406
407     indirect objects are the set of objects from expanding the
408     subsystem dependencies
409     '''
410
411     ret = getattr(t, 'indirect_objects', None)
412     if ret is not None: return ret
413
414     ret = set()
415     for lib in t.direct_objects:
416         if lib in chain:
417             dependency_loop(loops, t, lib)
418             continue
419         chain.add(lib)
420         t2 = bld.name_to_obj(lib, bld.env)
421         r2 = indirect_objects(bld, t2, chain, loops)
422         chain.remove(lib)
423         ret = ret.union(t2.direct_objects)
424         ret = ret.union(r2)
425
426     t.indirect_objects = ret
427     return ret
428
429
430 def extended_objects(bld, t, chain):
431     '''recursively calculate the extended object dependencies for a target
432
433     extended objects are the union of:
434        - direct objects
435        - indirect objects
436        - direct and indirect objects of all direct and indirect libraries
437     '''
438
439     ret = getattr(t, 'extended_objects', None)
440     if ret is not None: return ret
441
442     ret = set()
443     ret = ret.union(t.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     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
586         for t in tgt_list:
587             if t.samba_type != type: continue
588             # if we will indirectly link to a target then we don't need it
589             new = t.final_objects.copy()
590             for l in t.final_libs:
591                 t2 = bld.name_to_obj(l, bld.env)
592                 t2_obj = extended_objects(bld, t2, set())
593                 dup = new.intersection(t2_obj)
594                 if dup:
595                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
596                           t.sname, t.samba_type, dup, t2.samba_type, l)
597                     new = new.difference(dup)
598                     changed = True
599                     if not l in rely_on:
600                         rely_on[l] = set()
601                     rely_on[l] = rely_on[l].union(dup)
602             t.final_objects = new
603
604     # add back in any objects that were relied upon by the reduction rules
605     for r in rely_on:
606         t = bld.name_to_obj(r, bld.env)
607         t.final_objects = t.final_objects.union(rely_on[r])
608
609
610 def calculate_final_deps(bld, tgt_list, loops):
611     '''calculate the final library and object dependencies'''
612     for t in tgt_list:
613         # start with the maximum possible list
614         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
615         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
616
617     for t in tgt_list:
618         # don't depend on ourselves
619         if t.sname in t.final_libs:
620             t.final_libs.remove(t.sname)
621         if t.sname in t.final_objects:
622             t.final_objects.remove(t.sname)
623
624
625     # find any library loops
626     for t in tgt_list:
627         if t.samba_type in ['LIBRARY', 'PYTHON']:
628             for l in t.final_libs.copy():
629                 t2 = bld.name_to_obj(l, bld.env)
630                 if t.sname in t2.final_libs:
631                     # we could break this in either direction. If one of the libraries
632                     # has a version number, and will this be distributed publicly, then
633                     # we should make it the lower level library in the DAG
634                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
635                     dependency_loop(loops, t, t2.sname)
636                     t2.final_libs.remove(t.sname)
637
638
639     for loop in loops:
640         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
641
642     # we now need to make corrections for any library loops we broke up
643     # any target that depended on the target of the loop and doesn't
644     # depend on the source of the loop needs to get the loop source added
645     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
646         for t in tgt_list:
647             if t.samba_type != type: continue
648             for loop in loops:
649                 if loop in t.final_libs:
650                     diff = loops[loop].difference(t.final_libs)
651                     if t.sname in diff:
652                         diff.remove(t.sname)
653                     if diff:
654                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
655                         t.final_libs = t.final_libs.union(diff)
656
657     # remove objects that are also available in linked libs
658     reduce_objects(bld, tgt_list)
659     reduce_objects(bld, tgt_list)
660
661     # add in any syslib dependencies
662     for t in tgt_list:
663         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
664             continue
665         syslibs = set()
666         for d in t.final_objects:
667             t2 = bld.name_to_obj(d, bld.env)
668             syslibs = syslibs.union(t2.direct_syslibs)
669         # this adds the indirect syslibs as well, which may not be needed
670         # depending on the linker flags
671         for d in t.final_libs:
672             t2 = bld.name_to_obj(d, bld.env)
673             syslibs = syslibs.union(t2.direct_syslibs)
674         t.final_syslibs = syslibs
675
676     debug('deps: removed duplicate dependencies')
677
678
679 ######################################################################
680 # this provides a way to save our dependency calculations between runs
681 savedeps_version = 3
682 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
683 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
684 savedeps_outenv  = ['INC_PATHS']
685 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
686 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
687
688 def save_samba_deps(bld, tgt_list):
689     '''save the dependency calculations between builds, to make
690        further builds faster'''
691     denv = Environment.Environment()
692
693     denv.version = savedeps_version
694     denv.savedeps_inputs = savedeps_inputs
695     denv.savedeps_outputs = savedeps_outputs
696     denv.input = {}
697     denv.output = {}
698     denv.outenv = {}
699     denv.caches = {}
700     denv.files  = {}
701
702     for f in savedeps_files:
703         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
704
705     for c in savedeps_caches:
706         denv.caches[c] = LOCAL_CACHE(bld, c)
707
708     for t in tgt_list:
709         # save all the input attributes for each target
710         tdeps = {}
711         for attr in savedeps_inputs:
712             v = getattr(t, attr, None)
713             if v is not None:
714                 tdeps[attr] = v
715         if tdeps != {}:
716             denv.input[t.sname] = tdeps
717
718         # save all the output attributes for each target
719         tdeps = {}
720         for attr in savedeps_outputs:
721             v = getattr(t, attr, None)
722             if v is not None:
723                 tdeps[attr] = v
724         if tdeps != {}:
725             denv.output[t.sname] = tdeps
726
727         tdeps = {}
728         for attr in savedeps_outenv:
729             if attr in t.env:
730                 tdeps[attr] = t.env[attr]
731         if tdeps != {}:
732             denv.outenv[t.sname] = tdeps
733
734     depsfile = os.path.join(bld.bdir, "sambadeps")
735     denv.store(depsfile)
736
737
738
739 def load_samba_deps(bld, tgt_list):
740     '''load a previous set of build dependencies if possible'''
741     depsfile = os.path.join(bld.bdir, "sambadeps")
742     denv = Environment.Environment()
743     try:
744         debug('deps: checking saved dependencies')
745         denv.load(depsfile)
746         if (denv.version != savedeps_version or
747             denv.savedeps_inputs != savedeps_inputs or
748             denv.savedeps_outputs != savedeps_outputs):
749             return False
750     except:
751         return False
752
753     # check if critical files have changed
754     for f in savedeps_files:
755         if f not in denv.files:
756             return False
757         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
758             return False
759
760     # check if caches are the same
761     for c in savedeps_caches:
762         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
763             return False
764
765     # check inputs are the same
766     for t in tgt_list:
767         tdeps = {}
768         for attr in savedeps_inputs:
769             v = getattr(t, attr, None)
770             if v is not None:
771                 tdeps[attr] = v
772         if t.sname in denv.input:
773             olddeps = denv.input[t.sname]
774         else:
775             olddeps = {}
776         if tdeps != olddeps:
777             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
778             return False
779
780     # put outputs in place
781     for t in tgt_list:
782         if not t.sname in denv.output: continue
783         tdeps = denv.output[t.sname]
784         for a in tdeps:
785             setattr(t, a, tdeps[a])
786
787     # put output env vars in place
788     for t in tgt_list:
789         if not t.sname in denv.outenv: continue
790         tdeps = denv.outenv[t.sname]
791         for a in tdeps:
792             t.env[a] = tdeps[a]
793
794     debug('deps: loaded saved dependencies')
795     return True
796
797
798
799 def check_project_rules(bld):
800     '''check the project rules - ensuring the targets are sane'''
801
802     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
803     loops = {}
804     inc_loops = {}
805
806     # build a list of task generators we are interested in
807     tgt_list = []
808     for tgt in targets:
809         type = targets[tgt]
810         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
811             continue
812         t = bld.name_to_obj(tgt, bld.env)
813         if t is None:
814             print "Target %s of type %s has no task generator" % (tgt, type)
815             raise
816         tgt_list.append(t)
817
818     add_samba_attributes(bld, tgt_list)
819
820     if load_samba_deps(bld, tgt_list):
821         return
822
823     print "Checking project rules ..."
824
825     debug('deps: project rules checking started')
826
827     expand_subsystem_deps(bld)
828     build_direct_deps(bld, tgt_list)
829     break_dependency_loops(bld, tgt_list)
830     calculate_final_deps(bld, tgt_list, loops)
831
832     # run the various attribute generators
833     for f in [ build_dependencies, build_includes, add_init_functions ]:
834         debug('deps: project rules checking %s', f)
835         for t in tgt_list: f(t)
836
837     debug('deps: project rules stage1 completed')
838
839     #check_orpaned_targets(bld, tgt_list)
840
841     if not check_duplicate_sources(bld, tgt_list):
842         print "Duplicate sources present - aborting"
843         sys.exit(1)
844
845     show_final_deps(bld, tgt_list)
846
847     debug('deps: project rules checking completed - %u targets checked',
848           len(tgt_list))
849
850     save_samba_deps(bld, tgt_list)
851
852     print "Project rules pass"
853
854
855 def CHECK_PROJECT_RULES(bld):
856     '''enable checking of project targets for sanity'''
857     if bld.env.added_project_rules:
858         return
859     bld.env.added_project_rules = True
860     bld.add_pre_fun(check_project_rules)
861 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
862
863