build: the exceptions here are not useful
[samba.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         sys.exit(1)
22     cache[alias] = target
23 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
24
25
26 @conf
27 def SET_SYSLIB_DEPS(conf, target, deps):
28     '''setup some implied dependencies for a SYSLIB'''
29     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
30     cache[target] = deps
31
32
33 def EXPAND_ALIAS(bld, target):
34     '''expand a target name via an alias'''
35     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
36     if target in aliases:
37         return aliases[target]
38     return target
39 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
40
41
42 def expand_subsystem_deps(bld):
43     '''expand the reverse dependencies resulting from subsystem
44        attributes of modules'''
45     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
46     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
47     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
48
49     for s in subsystems:
50         if s in aliases:
51             s = aliases[s]
52         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
53         type = targets[s]
54         if type == 'DISABLED' or type == 'EMPTY':
55             continue
56
57         t = bld.name_to_obj(s, bld.env)
58         bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
59         for d in subsystems[s]:
60             type = targets[d['TARGET']]
61             if type != 'DISABLED' and type != 'EMPTY':
62                 t.samba_deps_extended.append(d['TARGET'])
63                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
64                 t2.samba_includes_extended.extend(t.samba_includes_extended)
65                 t2.samba_deps_extended.extend(t.samba_deps_extended)
66         t.samba_deps_extended = unique_list(t.samba_deps_extended)
67
68
69
70 def build_dependencies(self):
71     '''This builds the dependency list for a target. It runs after all the targets are declared
72
73     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
74     the full dependency list for a target until we have all of the targets declared.
75     '''
76
77     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
78         self.uselib        = list(self.final_syslibs)
79         self.uselib_local  = list(self.final_libs)
80         self.add_objects   = list(self.final_objects)
81
82         # extra link flags from pkg_config
83         libs = self.final_syslibs.copy()
84
85         (ccflags, ldflags) = library_flags(self, list(libs))
86         new_ldflags        = getattr(self, 'ldflags', [])
87         new_ldflags.extend(ldflags)
88         self.ldflags       = new_ldflags
89
90         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
91               self.sname, self.uselib, self.uselib_local, self.add_objects)
92
93     if self.samba_type in ['SUBSYSTEM']:
94         # this is needed for the ccflags of libs that come from pkg_config
95         self.uselib = list(self.direct_syslibs)
96
97     if getattr(self, 'uselib', None):
98         up_list = []
99         for l in self.uselib:
100            up_list.append(l.upper())
101         self.uselib = up_list
102
103 def build_includes(self):
104     '''This builds the right set of includes for a target.
105
106     One tricky part of this is that the includes= attribute for a
107     target needs to use paths which are relative to that targets
108     declaration directory (which we can get at via t.path).
109
110     The way this works is the includes list gets added as
111     samba_includes in the main build task declaration. Then this
112     function runs after all of the tasks are declared, and it
113     processes the samba_includes attribute to produce a includes=
114     attribute
115     '''
116
117     if getattr(self, 'samba_includes', None) is None:
118         return
119
120     bld = self.bld
121
122     inc_deps = includes_objects(bld, self, set(), {})
123
124     includes = []
125
126     # maybe add local includes
127     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
128         includes.append('.')
129
130     includes.extend(self.samba_includes_extended)
131
132     if 'EXTRA_INCLUDES' in bld.env:
133         includes.extend(bld.env['EXTRA_INCLUDES'])
134
135     includes.append('#')
136
137     inc_set = set()
138     inc_abs = []
139
140     for d in inc_deps:
141         t = bld.name_to_obj(d, bld.env)
142         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
143         inclist = getattr(t, 'samba_includes_extended', [])
144         if getattr(t, 'local_include', True) == True:
145             inclist.append('.')
146         if inclist == []:
147             continue
148         tpath = t.samba_abspath
149         for inc in inclist:
150             npath = tpath + '/' + inc
151             if not npath in inc_set:
152                 inc_abs.append(npath)
153                 inc_set.add(npath)
154
155     mypath = self.path.abspath(bld.env)
156     for inc in inc_abs:
157         relpath = os_path_relpath(inc, mypath)
158         includes.append(relpath)
159
160     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
161         includes.append('.')
162
163     # now transform the includes list to be relative to the top directory
164     # which is represented by '#' in waf. This allows waf to cache the
165     # includes lists more efficiently
166     includes_top = []
167     for i in includes:
168         if i[0] == '#':
169             # some are already top based
170             includes_top.append(i)
171             continue
172         absinc = os.path.join(self.path.abspath(), i)
173         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
174         includes_top.append('#' + relinc)
175
176     self.includes = unique_list(includes_top)
177     debug('deps: includes for target %s: includes=%s',
178           self.sname, self.includes)
179
180
181
182
183 def add_init_functions(self):
184     '''This builds the right set of init functions'''
185
186     bld = self.bld
187
188     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
189
190     # cope with the separated object lists from BINARY and LIBRARY targets
191     sname = self.sname
192     if sname.endswith('.objlist'):
193         sname = sname[0:-8]
194
195     modules = []
196     if sname in subsystems:
197         modules.append(sname)
198
199     m = getattr(self, 'samba_modules', None)
200     if m is not None:
201         modules.extend(TO_LIST(m))
202
203     m = getattr(self, 'samba_subsystem', None)
204     if m is not None:
205         modules.append(m)
206
207     if modules == []:
208         return
209
210     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
211
212     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
213
214     cflags = getattr(self, 'samba_cflags', [])[:]
215     for m in modules:
216         bld.ASSERT(m in subsystems,
217                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
218         init_fn_list = []
219         for d in subsystems[m]:
220             if targets[d['TARGET']] != 'DISABLED':
221                 init_fn_list.append(d['INIT_FUNCTION'])
222         if init_fn_list == []:
223             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
224         else:
225             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
226     self.ccflags = cflags
227
228
229
230 def check_duplicate_sources(bld, tgt_list):
231     '''see if we are compiling the same source file into multiple
232     subsystem targets for the same library or binary'''
233
234     debug('deps: checking for duplicate sources')
235
236     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
237     ret = True
238
239     seen = set()
240
241     for t in tgt_list:
242         obj_sources = getattr(t, 'source', '')
243         tpath = os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default')
244         obj_sources = bld.SUBDIR(tpath, obj_sources)
245         t.samba_source_set = set(TO_LIST(obj_sources))
246
247     for t in tgt_list:
248         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
249             continue
250
251         sources = []
252         for obj in t.add_objects:
253             t2 = t.bld.name_to_obj(obj, bld.env)
254             source_set = getattr(t2, 'samba_source_set', set())
255             sources.append( { 'dep':obj, 'src':source_set} )
256         for s in sources:
257             for s2 in sources:
258                 if s['dep'] == s2['dep']: continue
259                 common = s['src'].intersection(s2['src'])
260                 if common.difference(seen):
261                     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                 sys.exit(1)
333             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
334                 continue
335             if targets[d] == 'SYSLIB':
336                 t.direct_syslibs.add(d)
337                 if d in syslib_deps:
338                     for implied in TO_LIST(syslib_deps[d]):
339                         if BUILTIN_LIBRARY(bld, implied):
340                             t.direct_objects.add(implied)
341                         else:
342                             t.direct_libs.add(implied)
343                 continue
344             t2 = bld.name_to_obj(d, bld.env)
345             if t2 is None:
346                 print("no task %s type %s" % (d, targets[d]))
347             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
348                 t.direct_libs.add(d)
349             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
350                 t.direct_objects.add(d)
351     debug('deps: built direct dependencies')
352
353
354 def dependency_loop(loops, t, target):
355     '''add a dependency loop to the loops dictionary'''
356     if t.sname == target:
357         return
358     if not target in loops:
359         loops[target] = set()
360     if not t.sname in loops[target]:
361         loops[target].add(t.sname)
362
363
364 def indirect_libs(bld, t, chain, loops):
365     '''recursively calculate the indirect library dependencies for a target
366
367     An indirect library is a library that results from a dependency on
368     a subsystem
369     '''
370
371     ret = getattr(t, 'indirect_libs', None)
372     if ret is not None:
373         return ret
374
375     ret = set()
376     for obj in t.direct_objects:
377         if obj in chain:
378             dependency_loop(loops, t, obj)
379             continue
380         chain.add(obj)
381         t2 = bld.name_to_obj(obj, bld.env)
382         r2 = indirect_libs(bld, t2, chain, loops)
383         chain.remove(obj)
384         ret = ret.union(t2.direct_libs)
385         ret = ret.union(r2)
386
387     for obj in indirect_objects(bld, t, set(), loops):
388         if obj in chain:
389             dependency_loop(loops, t, obj)
390             continue
391         chain.add(obj)
392         t2 = bld.name_to_obj(obj, bld.env)
393         r2 = indirect_libs(bld, t2, chain, loops)
394         chain.remove(obj)
395         ret = ret.union(t2.direct_libs)
396         ret = ret.union(r2)
397
398     t.indirect_libs = ret
399
400     return ret
401
402
403 def indirect_objects(bld, t, chain, loops):
404     '''recursively calculate the indirect object dependencies for a target
405
406     indirect objects are the set of objects from expanding the
407     subsystem dependencies
408     '''
409
410     ret = getattr(t, 'indirect_objects', None)
411     if ret is not None: return ret
412
413     ret = set()
414     for lib in t.direct_objects:
415         if lib in chain:
416             dependency_loop(loops, t, lib)
417             continue
418         chain.add(lib)
419         t2 = bld.name_to_obj(lib, bld.env)
420         r2 = indirect_objects(bld, t2, chain, loops)
421         chain.remove(lib)
422         ret = ret.union(t2.direct_objects)
423         ret = ret.union(r2)
424
425     t.indirect_objects = ret
426     return ret
427
428
429 def extended_objects(bld, t, chain):
430     '''recursively calculate the extended object dependencies for a target
431
432     extended objects are the union of:
433        - direct objects
434        - indirect objects
435        - direct and indirect objects of all direct and indirect libraries
436     '''
437
438     ret = getattr(t, 'extended_objects', None)
439     if ret is not None: return ret
440
441     ret = set()
442     ret = ret.union(t.final_objects)
443
444     for lib in t.final_libs:
445         if lib in chain:
446             continue
447         t2 = bld.name_to_obj(lib, bld.env)
448         chain.add(lib)
449         r2 = extended_objects(bld, t2, chain)
450         chain.remove(lib)
451         ret = ret.union(t2.final_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     for loop in inc_loops:
518         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
519
520     # expand the loops mapping by one level
521     for loop in loops.copy():
522         for tgt in loops[loop]:
523             if tgt in loops:
524                 loops[loop] = loops[loop].union(loops[tgt])
525
526     for loop in inc_loops.copy():
527         for tgt in inc_loops[loop]:
528             if tgt in inc_loops:
529                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
530
531
532     # expand indirect subsystem and library loops
533     for loop in loops.copy():
534         t = bld.name_to_obj(loop, bld.env)
535         if t.samba_type in ['SUBSYSTEM']:
536             loops[loop] = loops[loop].union(t.indirect_objects)
537             loops[loop] = loops[loop].union(t.direct_objects)
538         if t.samba_type in ['LIBRARY','PYTHON']:
539             loops[loop] = loops[loop].union(t.indirect_libs)
540             loops[loop] = loops[loop].union(t.direct_libs)
541         if loop in loops[loop]:
542             loops[loop].remove(loop)
543
544     # expand indirect includes loops
545     for loop in inc_loops.copy():
546         t = bld.name_to_obj(loop, bld.env)
547         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
548         if loop in inc_loops[loop]:
549             inc_loops[loop].remove(loop)
550
551     # add in the replacement dependencies
552     for t in tgt_list:
553         for loop in loops:
554             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
555                 objs = getattr(t, attr, set())
556                 if loop in objs:
557                     diff = loops[loop].difference(objs)
558                     if t.sname in diff:
559                         diff.remove(t.sname)
560                     if diff:
561                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
562                         objs = objs.union(diff)
563                 setattr(t, attr, objs)
564
565         for loop in inc_loops:
566             objs = getattr(t, 'includes_objects', set())
567             if loop in objs:
568                 diff = inc_loops[loop].difference(objs)
569                 if t.sname in diff:
570                     diff.remove(t.sname)
571                 if diff:
572                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
573                     objs = objs.union(diff)
574             setattr(t, 'includes_objects', objs)
575
576
577 def reduce_objects(bld, tgt_list):
578     '''reduce objects by looking for indirect object dependencies'''
579     rely_on = {}
580
581     for t in tgt_list:
582         t.extended_objects = None
583
584     changed = False
585
586     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
587         for t in tgt_list:
588             if t.samba_type != type: continue
589             # if we will indirectly link to a target then we don't need it
590             new = t.final_objects.copy()
591             for l in t.final_libs:
592                 t2 = bld.name_to_obj(l, bld.env)
593                 t2_obj = extended_objects(bld, t2, set())
594                 dup = new.intersection(t2_obj)
595                 if dup:
596                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
597                           t.sname, t.samba_type, dup, t2.samba_type, l)
598                     new = new.difference(dup)
599                     changed = True
600                     if not l in rely_on:
601                         rely_on[l] = set()
602                     rely_on[l] = rely_on[l].union(dup)
603             t.final_objects = new
604
605     if not changed:
606         return False
607
608     # add back in any objects that were relied upon by the reduction rules
609     for r in rely_on:
610         t = bld.name_to_obj(r, bld.env)
611         t.final_objects = t.final_objects.union(rely_on[r])
612
613     return True
614
615
616 def calculate_final_deps(bld, tgt_list, loops):
617     '''calculate the final library and object dependencies'''
618     for t in tgt_list:
619         # start with the maximum possible list
620         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
621         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
622
623     for t in tgt_list:
624         # don't depend on ourselves
625         if t.sname in t.final_libs:
626             t.final_libs.remove(t.sname)
627         if t.sname in t.final_objects:
628             t.final_objects.remove(t.sname)
629
630
631     # find any library loops
632     for t in tgt_list:
633         if t.samba_type in ['LIBRARY', 'PYTHON']:
634             for l in t.final_libs.copy():
635                 t2 = bld.name_to_obj(l, bld.env)
636                 if t.sname in t2.final_libs:
637                     # we could break this in either direction. If one of the libraries
638                     # has a version number, and will this be distributed publicly, then
639                     # we should make it the lower level library in the DAG
640                     debug('deps: removing library loop %s from %s', t.sname, t2.sname)
641                     dependency_loop(loops, t, t2.sname)
642                     t2.final_libs.remove(t.sname)
643
644
645     for loop in loops:
646         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
647
648     # we now need to make corrections for any library loops we broke up
649     # any target that depended on the target of the loop and doesn't
650     # depend on the source of the loop needs to get the loop source added
651     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
652         for t in tgt_list:
653             if t.samba_type != type: continue
654             for loop in loops:
655                 if loop in t.final_libs:
656                     diff = loops[loop].difference(t.final_libs)
657                     if t.sname in diff:
658                         diff.remove(t.sname)
659                     if diff:
660                         debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
661                         t.final_libs = t.final_libs.union(diff)
662
663     # remove objects that are also available in linked libs
664     count = 0
665     while reduce_objects(bld, tgt_list):
666         count += 1
667         if count > 100:
668             print("WARNING: Unable to remove all inter-target object duplicates")
669             break
670     debug('deps: Object reduction took %u iterations', count)
671
672     # add in any syslib dependencies
673     for t in tgt_list:
674         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
675             continue
676         syslibs = set()
677         for d in t.final_objects:
678             t2 = bld.name_to_obj(d, bld.env)
679             syslibs = syslibs.union(t2.direct_syslibs)
680         # this adds the indirect syslibs as well, which may not be needed
681         # depending on the linker flags
682         for d in t.final_libs:
683             t2 = bld.name_to_obj(d, bld.env)
684             syslibs = syslibs.union(t2.direct_syslibs)
685         t.final_syslibs = syslibs
686
687     debug('deps: removed duplicate dependencies')
688
689
690 ######################################################################
691 # this provides a way to save our dependency calculations between runs
692 savedeps_version = 3
693 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
694 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
695 savedeps_outenv  = ['INC_PATHS']
696 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
697 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
698
699 def save_samba_deps(bld, tgt_list):
700     '''save the dependency calculations between builds, to make
701        further builds faster'''
702     denv = Environment.Environment()
703
704     denv.version = savedeps_version
705     denv.savedeps_inputs = savedeps_inputs
706     denv.savedeps_outputs = savedeps_outputs
707     denv.input = {}
708     denv.output = {}
709     denv.outenv = {}
710     denv.caches = {}
711     denv.files  = {}
712
713     for f in savedeps_files:
714         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
715
716     for c in savedeps_caches:
717         denv.caches[c] = LOCAL_CACHE(bld, c)
718
719     for t in tgt_list:
720         # save all the input attributes for each target
721         tdeps = {}
722         for attr in savedeps_inputs:
723             v = getattr(t, attr, None)
724             if v is not None:
725                 tdeps[attr] = v
726         if tdeps != {}:
727             denv.input[t.sname] = tdeps
728
729         # save all the output attributes for each target
730         tdeps = {}
731         for attr in savedeps_outputs:
732             v = getattr(t, attr, None)
733             if v is not None:
734                 tdeps[attr] = v
735         if tdeps != {}:
736             denv.output[t.sname] = tdeps
737
738         tdeps = {}
739         for attr in savedeps_outenv:
740             if attr in t.env:
741                 tdeps[attr] = t.env[attr]
742         if tdeps != {}:
743             denv.outenv[t.sname] = tdeps
744
745     depsfile = os.path.join(bld.bdir, "sambadeps")
746     denv.store(depsfile)
747
748
749
750 def load_samba_deps(bld, tgt_list):
751     '''load a previous set of build dependencies if possible'''
752     depsfile = os.path.join(bld.bdir, "sambadeps")
753     denv = Environment.Environment()
754     try:
755         debug('deps: checking saved dependencies')
756         denv.load(depsfile)
757         if (denv.version != savedeps_version or
758             denv.savedeps_inputs != savedeps_inputs or
759             denv.savedeps_outputs != savedeps_outputs):
760             return False
761     except:
762         return False
763
764     # check if critical files have changed
765     for f in savedeps_files:
766         if f not in denv.files:
767             return False
768         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
769             return False
770
771     # check if caches are the same
772     for c in savedeps_caches:
773         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
774             return False
775
776     # check inputs are the same
777     for t in tgt_list:
778         tdeps = {}
779         for attr in savedeps_inputs:
780             v = getattr(t, attr, None)
781             if v is not None:
782                 tdeps[attr] = v
783         if t.sname in denv.input:
784             olddeps = denv.input[t.sname]
785         else:
786             olddeps = {}
787         if tdeps != olddeps:
788             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
789             return False
790
791     # put outputs in place
792     for t in tgt_list:
793         if not t.sname in denv.output: continue
794         tdeps = denv.output[t.sname]
795         for a in tdeps:
796             setattr(t, a, tdeps[a])
797
798     # put output env vars in place
799     for t in tgt_list:
800         if not t.sname in denv.outenv: continue
801         tdeps = denv.outenv[t.sname]
802         for a in tdeps:
803             t.env[a] = tdeps[a]
804
805     debug('deps: loaded saved dependencies')
806     return True
807
808
809
810 def check_project_rules(bld):
811     '''check the project rules - ensuring the targets are sane'''
812
813     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
814     loops = {}
815     inc_loops = {}
816
817     # build a list of task generators we are interested in
818     tgt_list = []
819     for tgt in targets:
820         type = targets[tgt]
821         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
822             continue
823         t = bld.name_to_obj(tgt, bld.env)
824         if t is None:
825             print("Target %s of type %s has no task generator" % (tgt, type))
826             sys.exit(1)
827         tgt_list.append(t)
828
829     add_samba_attributes(bld, tgt_list)
830
831     if load_samba_deps(bld, tgt_list):
832         return
833
834     print("Checking project rules ...")
835
836     debug('deps: project rules checking started')
837
838     expand_subsystem_deps(bld)
839     build_direct_deps(bld, tgt_list)
840     break_dependency_loops(bld, tgt_list)
841     calculate_final_deps(bld, tgt_list, loops)
842
843     # run the various attribute generators
844     for f in [ build_dependencies, build_includes, add_init_functions ]:
845         debug('deps: project rules checking %s', f)
846         for t in tgt_list: f(t)
847
848     debug('deps: project rules stage1 completed')
849
850     #check_orpaned_targets(bld, tgt_list)
851
852     if not check_duplicate_sources(bld, tgt_list):
853         print("Duplicate sources present - aborting")
854         sys.exit(1)
855
856     show_final_deps(bld, tgt_list)
857
858     debug('deps: project rules checking completed - %u targets checked',
859           len(tgt_list))
860
861     save_samba_deps(bld, tgt_list)
862
863     print("Project rules pass")
864
865
866 def CHECK_PROJECT_RULES(bld):
867     '''enable checking of project targets for sanity'''
868     if bld.env.added_project_rules:
869         return
870     bld.env.added_project_rules = True
871     bld.add_pre_fun(check_project_rules)
872 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
873
874