553a91a81adc30bde91c2512dc9442eb1febf71a
[kai/samba.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, re, Environment, Logs
4 from samba_utils import *
5 from samba_autoconf import *
6 from samba_bundled import BUILTIN_LIBRARY
7
8 @conf
9 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
10     '''add a dependency for all binaries and libraries'''
11     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
12         ctx.env.GLOBAL_DEPENDENCIES = []
13     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
14
15
16 @conf
17 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx):
18     '''indicate that circular dependencies between libraries should be broken.'''
19     ctx.env.ALLOW_CIRCULAR_LIB_DEPENDENCIES = True
20
21
22 def TARGET_ALIAS(bld, target, alias):
23     '''define an alias for a target name'''
24     cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
25     if alias in cache:
26         Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
27         sys.exit(1)
28     cache[alias] = target
29 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
30
31
32 @conf
33 def SET_SYSLIB_DEPS(conf, target, deps):
34     '''setup some implied dependencies for a SYSLIB'''
35     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
36     cache[target] = deps
37
38
39 def EXPAND_ALIAS(bld, target):
40     '''expand a target name via an alias'''
41     aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
42     if target in aliases:
43         return aliases[target]
44     return target
45 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
46
47
48 def expand_subsystem_deps(bld):
49     '''expand the reverse dependencies resulting from subsystem
50        attributes of modules'''
51     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
52     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
53     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
54
55     for s in subsystems:
56         if s in aliases:
57             s = aliases[s]
58         bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
59         type = targets[s]
60         if type == 'DISABLED' or type == 'EMPTY':
61             continue
62
63         t = bld.name_to_obj(s, bld.env)
64         for d in subsystems[s]:
65             type = targets[d['TARGET']]
66             if type != 'DISABLED' and type != 'EMPTY':
67                 bld.ASSERT(t is not None,
68                     "Subsystem target %s for %s (%s) not found" % (s, d['TARGET'], type))
69                 t.samba_deps_extended.append(d['TARGET'])
70                 t2 = bld.name_to_obj(d['TARGET'], bld.env)
71                 t2.samba_includes_extended.extend(t.samba_includes_extended)
72                 t2.samba_deps_extended.extend(t.samba_deps_extended)
73         t.samba_deps_extended = unique_list(t.samba_deps_extended)
74
75
76
77 def build_dependencies(self):
78     '''This builds the dependency list for a target. It runs after all the targets are declared
79
80     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
81     the full dependency list for a target until we have all of the targets declared.
82     '''
83
84     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
85         self.uselib        = list(self.final_syslibs)
86         self.uselib_local  = list(self.final_libs)
87         self.add_objects   = list(self.final_objects)
88
89         # extra link flags from pkg_config
90         libs = self.final_syslibs.copy()
91
92         (ccflags, ldflags) = library_flags(self, list(libs))
93         new_ldflags        = getattr(self, 'ldflags', [])
94         new_ldflags.extend(ldflags)
95         self.ldflags       = new_ldflags
96
97         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
98               self.sname, self.uselib, self.uselib_local, self.add_objects)
99
100     if self.samba_type in ['SUBSYSTEM']:
101         # this is needed for the ccflags of libs that come from pkg_config
102         self.uselib = list(self.direct_syslibs)
103
104     if getattr(self, 'uselib', None):
105         up_list = []
106         for l in self.uselib:
107            up_list.append(l.upper())
108         self.uselib = up_list
109
110 def build_includes(self):
111     '''This builds the right set of includes for a target.
112
113     One tricky part of this is that the includes= attribute for a
114     target needs to use paths which are relative to that targets
115     declaration directory (which we can get at via t.path).
116
117     The way this works is the includes list gets added as
118     samba_includes in the main build task declaration. Then this
119     function runs after all of the tasks are declared, and it
120     processes the samba_includes attribute to produce a includes=
121     attribute
122     '''
123
124     if getattr(self, 'samba_includes', None) is None:
125         return
126
127     bld = self.bld
128
129     inc_deps = includes_objects(bld, self, set(), {})
130
131     includes = []
132
133     # maybe add local includes
134     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
135         includes.append('.')
136
137     includes.extend(self.samba_includes_extended)
138
139     if 'EXTRA_INCLUDES' in bld.env:
140         includes.extend(bld.env['EXTRA_INCLUDES'])
141
142     includes.append('#')
143
144     inc_set = set()
145     inc_abs = []
146
147     for d in inc_deps:
148         t = bld.name_to_obj(d, bld.env)
149         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
150         inclist = getattr(t, 'samba_includes_extended', [])
151         if getattr(t, 'local_include', True) == True:
152             inclist.append('.')
153         if inclist == []:
154             continue
155         tpath = t.samba_abspath
156         for inc in inclist:
157             npath = tpath + '/' + inc
158             if not npath in inc_set:
159                 inc_abs.append(npath)
160                 inc_set.add(npath)
161
162     mypath = self.path.abspath(bld.env)
163     for inc in inc_abs:
164         relpath = os_path_relpath(inc, mypath)
165         includes.append(relpath)
166
167     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
168         includes.append('.')
169
170     # now transform the includes list to be relative to the top directory
171     # which is represented by '#' in waf. This allows waf to cache the
172     # includes lists more efficiently
173     includes_top = []
174     for i in includes:
175         if i[0] == '#':
176             # some are already top based
177             includes_top.append(i)
178             continue
179         absinc = os.path.join(self.path.abspath(), i)
180         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
181         includes_top.append('#' + relinc)
182
183     self.includes = unique_list(includes_top)
184     debug('deps: includes for target %s: includes=%s',
185           self.sname, self.includes)
186
187
188
189
190 def add_init_functions(self):
191     '''This builds the right set of init functions'''
192
193     bld = self.bld
194
195     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
196
197     # cope with the separated object lists from BINARY and LIBRARY targets
198     sname = self.sname
199     if sname.endswith('.objlist'):
200         sname = sname[0:-8]
201
202     modules = []
203     if sname in subsystems:
204         modules.append(sname)
205
206     m = getattr(self, 'samba_modules', None)
207     if m is not None:
208         modules.extend(TO_LIST(m))
209
210     m = getattr(self, 'samba_subsystem', None)
211     if m is not None:
212         modules.append(m)
213
214     if modules == []:
215         return
216
217     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
218
219     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
220
221     cflags = getattr(self, 'samba_cflags', [])[:]
222     for m in modules:
223         bld.ASSERT(m in subsystems,
224                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
225         init_fn_list = []
226         for d in subsystems[m]:
227             if targets[d['TARGET']] != 'DISABLED':
228                 init_fn_list.append(d['INIT_FUNCTION'])
229         if init_fn_list == []:
230             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
231         else:
232             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
233     self.ccflags = cflags
234
235
236
237 def check_duplicate_sources(bld, tgt_list):
238     '''see if we are compiling the same source file into multiple
239     subsystem targets for the same library or binary'''
240
241     debug('deps: checking for duplicate sources')
242
243     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
244     ret = True
245
246     seen = set()
247
248     for t in tgt_list:
249         obj_sources = getattr(t, 'source', '')
250         tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
251         obj_sources = bld.SUBDIR(tpath, obj_sources)
252         t.samba_source_set = set(TO_LIST(obj_sources))
253
254     for t in tgt_list:
255         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
256             continue
257
258         sources = []
259         for obj in t.add_objects:
260             t2 = t.bld.name_to_obj(obj, bld.env)
261             source_set = getattr(t2, 'samba_source_set', set())
262             sources.append( { 'dep':obj, 'src':source_set} )
263         for s in sources:
264             for s2 in sources:
265                 if s['dep'] == s2['dep']: continue
266                 common = s['src'].intersection(s2['src'])
267                 if common.difference(seen):
268                     Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
269                                                                                       s['dep'], s2['dep'],
270                                                                                       common))
271                     seen = seen.union(common)
272                     ret = False
273     return ret
274
275
276 def check_orpaned_targets(bld, tgt_list):
277     '''check if any build targets are orphaned'''
278
279     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
280
281     debug('deps: checking for orphaned targets')
282
283     for t in tgt_list:
284         if getattr(t, 'samba_used', False) == True:
285             continue
286         type = target_dict[t.sname]
287         if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
288             if re.search('^PIDL_', t.sname) is None:
289                 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
290
291
292 def check_group_ordering(bld, tgt_list):
293     '''see if we have any dependencies that violate the group ordering
294
295     It is an error for a target to depend on a target from a later
296     build group
297     '''
298
299     def group_name(g):
300         tm = bld.task_manager
301         return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
302
303     for g in bld.task_manager.groups:
304         gname = group_name(g)
305         for t in g.tasks_gen:
306             t.samba_group = gname
307
308     grp_map = {}
309     idx = 0
310     for g in bld.task_manager.groups:
311         name = group_name(g)
312         grp_map[name] = idx
313         idx += 1
314
315     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
316
317     ret = True
318     for t in tgt_list:
319         tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
320         for d in tdeps:
321             t2 = bld.name_to_obj(d, bld.env)
322             if t2 is None:
323                 continue
324             map1 = grp_map[t.samba_group]
325             map2 = grp_map[t2.samba_group]
326
327             if map2 > map1:
328                 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
329                            t.sname, t.samba_group, t2.sname, t2.samba_group))
330                 ret = False
331
332     return ret
333
334
335 def show_final_deps(bld, tgt_list):
336     '''show the final dependencies for all targets'''
337
338     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
339
340     for t in tgt_list:
341         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
342             continue
343         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
344               t.sname, t.uselib, t.uselib_local, t.add_objects)
345
346
347 def add_samba_attributes(bld, tgt_list):
348     '''ensure a target has a the required samba attributes'''
349
350     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
351
352     for t in tgt_list:
353         if t.name != '':
354             t.sname = t.name
355         else:
356             t.sname = t.target
357         t.samba_type = targets[t.sname]
358         t.samba_abspath = t.path.abspath(bld.env)
359         t.samba_deps_extended = t.samba_deps[:]
360         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
361         t.ccflags = getattr(t, 'samba_cflags', '')
362
363
364 def build_direct_deps(bld, tgt_list):
365     '''build the direct_objects and direct_libs sets for each target'''
366
367     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
368     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
369     global_deps = bld.env.GLOBAL_DEPENDENCIES
370
371     for t in tgt_list:
372         t.direct_objects = set()
373         t.direct_libs = set()
374         t.direct_syslibs = set()
375         deps = t.samba_deps_extended
376         if getattr(t, 'samba_use_global_deps', False):
377             deps.extend(global_deps)
378         for d in deps:
379             d = EXPAND_ALIAS(bld, d)
380             if d == t.sname: continue
381             if not d in targets:
382                 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
383                 sys.exit(1)
384             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
385                 continue
386             if targets[d] == 'SYSLIB':
387                 t.direct_syslibs.add(d)
388                 if d in syslib_deps:
389                     for implied in TO_LIST(syslib_deps[d]):
390                         if BUILTIN_LIBRARY(bld, implied):
391                             t.direct_objects.add(implied)
392                         elif targets[implied] == 'SYSLIB':
393                             t.direct_syslibs.add(implied)
394                         elif targets[implied] in ['LIBRARY', 'MODULE']:
395                             t.direct_libs.add(implied)
396                         else:
397                             Logs.error('Implied dependency %s in %s is of type %s' % (
398                                 implied, t.sname, targets[implied]))
399                             sys.exit(1)
400                 continue
401             t2 = bld.name_to_obj(d, bld.env)
402             if t2 is None:
403                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
404                 sys.exit(1)
405             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
406                 t.direct_libs.add(d)
407             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
408                 t.direct_objects.add(d)
409     debug('deps: built direct dependencies')
410
411
412 def dependency_loop(loops, t, target):
413     '''add a dependency loop to the loops dictionary'''
414     if t.sname == target:
415         return
416     if not target in loops:
417         loops[target] = set()
418     if not t.sname in loops[target]:
419         loops[target].add(t.sname)
420
421
422 def indirect_libs(bld, t, chain, loops):
423     '''recursively calculate the indirect library dependencies for a target
424
425     An indirect library is a library that results from a dependency on
426     a subsystem
427     '''
428
429     ret = getattr(t, 'indirect_libs', None)
430     if ret is not None:
431         return ret
432
433     ret = set()
434     for obj in t.direct_objects:
435         if obj in chain:
436             dependency_loop(loops, t, obj)
437             continue
438         chain.add(obj)
439         t2 = bld.name_to_obj(obj, bld.env)
440         r2 = indirect_libs(bld, t2, chain, loops)
441         chain.remove(obj)
442         ret = ret.union(t2.direct_libs)
443         ret = ret.union(r2)
444
445     for obj in indirect_objects(bld, t, set(), loops):
446         if obj in chain:
447             dependency_loop(loops, t, obj)
448             continue
449         chain.add(obj)
450         t2 = bld.name_to_obj(obj, bld.env)
451         r2 = indirect_libs(bld, t2, chain, loops)
452         chain.remove(obj)
453         ret = ret.union(t2.direct_libs)
454         ret = ret.union(r2)
455
456     t.indirect_libs = ret
457
458     return ret
459
460
461 def indirect_objects(bld, t, chain, loops):
462     '''recursively calculate the indirect object dependencies for a target
463
464     indirect objects are the set of objects from expanding the
465     subsystem dependencies
466     '''
467
468     ret = getattr(t, 'indirect_objects', None)
469     if ret is not None: return ret
470
471     ret = set()
472     for lib in t.direct_objects:
473         if lib in chain:
474             dependency_loop(loops, t, lib)
475             continue
476         chain.add(lib)
477         t2 = bld.name_to_obj(lib, bld.env)
478         r2 = indirect_objects(bld, t2, chain, loops)
479         chain.remove(lib)
480         ret = ret.union(t2.direct_objects)
481         ret = ret.union(r2)
482
483     t.indirect_objects = ret
484     return ret
485
486
487 def extended_objects(bld, t, chain):
488     '''recursively calculate the extended object dependencies for a target
489
490     extended objects are the union of:
491        - direct objects
492        - indirect objects
493        - direct and indirect objects of all direct and indirect libraries
494     '''
495
496     ret = getattr(t, 'extended_objects', None)
497     if ret is not None: return ret
498
499     ret = set()
500     ret = ret.union(t.final_objects)
501
502     for lib in t.final_libs:
503         if lib in chain:
504             continue
505         t2 = bld.name_to_obj(lib, bld.env)
506         chain.add(lib)
507         r2 = extended_objects(bld, t2, chain)
508         chain.remove(lib)
509         ret = ret.union(t2.final_objects)
510         ret = ret.union(r2)
511
512     t.extended_objects = ret
513     return ret
514
515
516 def includes_objects(bld, t, chain, inc_loops):
517     '''recursively calculate the includes object dependencies for a target
518
519     includes dependencies come from either library or object dependencies
520     '''
521     ret = getattr(t, 'includes_objects', None)
522     if ret is not None:
523         return ret
524
525     ret = t.direct_objects.copy()
526     ret = ret.union(t.direct_libs)
527
528     for obj in t.direct_objects:
529         if obj in chain:
530             dependency_loop(inc_loops, t, obj)
531             continue
532         chain.add(obj)
533         t2 = bld.name_to_obj(obj, bld.env)
534         r2 = includes_objects(bld, t2, chain, inc_loops)
535         chain.remove(obj)
536         ret = ret.union(t2.direct_objects)
537         ret = ret.union(r2)
538
539     for lib in t.direct_libs:
540         if lib in chain:
541             dependency_loop(inc_loops, t, lib)
542             continue
543         chain.add(lib)
544         t2 = bld.name_to_obj(lib, bld.env)
545         if t2 is None:
546             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
547             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
548                 lib, targets[lib], t.sname))
549             sys.exit(1)
550         r2 = includes_objects(bld, t2, chain, inc_loops)
551         chain.remove(lib)
552         ret = ret.union(t2.direct_objects)
553         ret = ret.union(r2)
554
555     t.includes_objects = ret
556     return ret
557
558
559 def break_dependency_loops(bld, tgt_list):
560     '''find and break dependency loops'''
561     loops = {}
562     inc_loops = {}
563
564     # build up the list of loops
565     for t in tgt_list:
566         indirect_objects(bld, t, set(), loops)
567         indirect_libs(bld, t, set(), loops)
568         includes_objects(bld, t, set(), inc_loops)
569
570     # break the loops
571     for t in tgt_list:
572         if t.sname in loops:
573             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
574                 objs = getattr(t, attr, set())
575                 setattr(t, attr, objs.difference(loops[t.sname]))
576
577     for loop in loops:
578         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
579
580     for loop in inc_loops:
581         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
582
583     # expand the loops mapping by one level
584     for loop in loops.copy():
585         for tgt in loops[loop]:
586             if tgt in loops:
587                 loops[loop] = loops[loop].union(loops[tgt])
588
589     for loop in inc_loops.copy():
590         for tgt in inc_loops[loop]:
591             if tgt in inc_loops:
592                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
593
594
595     # expand indirect subsystem and library loops
596     for loop in loops.copy():
597         t = bld.name_to_obj(loop, bld.env)
598         if t.samba_type in ['SUBSYSTEM']:
599             loops[loop] = loops[loop].union(t.indirect_objects)
600             loops[loop] = loops[loop].union(t.direct_objects)
601         if t.samba_type in ['LIBRARY','PYTHON']:
602             loops[loop] = loops[loop].union(t.indirect_libs)
603             loops[loop] = loops[loop].union(t.direct_libs)
604         if loop in loops[loop]:
605             loops[loop].remove(loop)
606
607     # expand indirect includes loops
608     for loop in inc_loops.copy():
609         t = bld.name_to_obj(loop, bld.env)
610         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
611         if loop in inc_loops[loop]:
612             inc_loops[loop].remove(loop)
613
614     # add in the replacement dependencies
615     for t in tgt_list:
616         for loop in loops:
617             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
618                 objs = getattr(t, attr, set())
619                 if loop in objs:
620                     diff = loops[loop].difference(objs)
621                     if t.sname in diff:
622                         diff.remove(t.sname)
623                     if diff:
624                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
625                         objs = objs.union(diff)
626                 setattr(t, attr, objs)
627
628         for loop in inc_loops:
629             objs = getattr(t, 'includes_objects', set())
630             if loop in objs:
631                 diff = inc_loops[loop].difference(objs)
632                 if t.sname in diff:
633                     diff.remove(t.sname)
634                 if diff:
635                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
636                     objs = objs.union(diff)
637             setattr(t, 'includes_objects', objs)
638
639
640 def reduce_objects(bld, tgt_list):
641     '''reduce objects by looking for indirect object dependencies'''
642     rely_on = {}
643
644     for t in tgt_list:
645         t.extended_objects = None
646
647     changed = False
648
649     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
650         for t in tgt_list:
651             if t.samba_type != type: continue
652             # if we will indirectly link to a target then we don't need it
653             new = t.final_objects.copy()
654             for l in t.final_libs:
655                 t2 = bld.name_to_obj(l, bld.env)
656                 t2_obj = extended_objects(bld, t2, set())
657                 dup = new.intersection(t2_obj)
658                 if t.sname in rely_on:
659                     dup = dup.difference(rely_on[t.sname])
660                 if dup:
661                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
662                           t.sname, t.samba_type, dup, t2.samba_type, l)
663                     new = new.difference(dup)
664                     changed = True
665                     if not l in rely_on:
666                         rely_on[l] = set()
667                     rely_on[l] = rely_on[l].union(dup)
668             t.final_objects = new
669
670     if not changed:
671         return False
672
673     # add back in any objects that were relied upon by the reduction rules
674     for r in rely_on:
675         t = bld.name_to_obj(r, bld.env)
676         t.final_objects = t.final_objects.union(rely_on[r])
677
678     return True
679
680
681 def calculate_final_deps(bld, tgt_list, loops):
682     '''calculate the final library and object dependencies'''
683     for t in tgt_list:
684         # start with the maximum possible list
685         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
686         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
687
688     for t in tgt_list:
689         # don't depend on ourselves
690         if t.sname in t.final_libs:
691             t.final_libs.remove(t.sname)
692         if t.sname in t.final_objects:
693             t.final_objects.remove(t.sname)
694
695     # handle any non-shared binaries
696     for t in tgt_list:
697         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
698             # replace lib deps with objlist deps
699             for l in t.final_libs:
700                 objname = l + '.objlist'
701                 t2 = bld.name_to_obj(objname, bld.env)
702                 if t2 is None:
703                     Logs.error('ERROR: subsystem %s not found' % objname)
704                     sys.exit(1)
705                 t.final_objects.add(objname)
706                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
707             t.final_libs = set()
708
709     # find any library loops
710     for t in tgt_list:
711         if t.samba_type in ['LIBRARY', 'PYTHON']:
712             for l in t.final_libs.copy():
713                 t2 = bld.name_to_obj(l, bld.env)
714                 if t.sname in t2.final_libs:
715                     if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
716                         # we could break this in either direction. If one of the libraries
717                         # has a version number, and will this be distributed publicly, then
718                         # we should make it the lower level library in the DAG
719                         Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
720                         dependency_loop(loops, t, t2.sname)
721                         t2.final_libs.remove(t.sname)
722                     else:
723                         Logs.error('ERROR: circular library dependency between %s and %s'
724                             % (t.sname, t2.sname))
725                         sys.exit(1)
726
727     for loop in loops:
728         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
729
730     # we now need to make corrections for any library loops we broke up
731     # any target that depended on the target of the loop and doesn't
732     # depend on the source of the loop needs to get the loop source added
733     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
734         for t in tgt_list:
735             if t.samba_type != type: continue
736             for loop in loops:
737                 if loop in t.final_libs:
738                     diff = loops[loop].difference(t.final_libs)
739                     if t.sname in diff:
740                         diff.remove(t.sname)
741                     if t.sname in diff:
742                         diff.remove(t.sname)
743                     # make sure we don't recreate the loop again!
744                     for d in diff.copy():
745                         t2 = bld.name_to_obj(d, bld.env)
746                         if t2.samba_type == 'LIBRARY':
747                             if t.sname in t2.final_libs:
748                                 debug('deps: removing expansion %s from %s', d, t.sname)
749                                 diff.remove(d)
750                     if diff:
751                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
752                               loops[loop], diff)
753                         t.final_libs = t.final_libs.union(diff)
754
755     # remove objects that are also available in linked libs
756     count = 0
757     while reduce_objects(bld, tgt_list):
758         count += 1
759         if count > 100:
760             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
761             break
762     debug('deps: Object reduction took %u iterations', count)
763
764     # add in any syslib dependencies
765     for t in tgt_list:
766         if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
767             continue
768         syslibs = set()
769         for d in t.final_objects:
770             t2 = bld.name_to_obj(d, bld.env)
771             syslibs = syslibs.union(t2.direct_syslibs)
772         # this adds the indirect syslibs as well, which may not be needed
773         # depending on the linker flags
774         for d in t.final_libs:
775             t2 = bld.name_to_obj(d, bld.env)
776             syslibs = syslibs.union(t2.direct_syslibs)
777         t.final_syslibs = syslibs
778
779
780     # find any unresolved library loops
781     lib_loop_error = False
782     for t in tgt_list:
783         if t.samba_type in ['LIBRARY', 'PYTHON']:
784             for l in t.final_libs.copy():
785                 t2 = bld.name_to_obj(l, bld.env)
786                 if t.sname in t2.final_libs:
787                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
788                     lib_loop_error = True
789     if lib_loop_error:
790         sys.exit(1)
791
792     debug('deps: removed duplicate dependencies')
793
794
795 ######################################################################
796 # this provides a way to save our dependency calculations between runs
797 savedeps_version = 3
798 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
799 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
800 savedeps_outenv  = ['INC_PATHS']
801 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES']
802 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
803 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
804
805 def save_samba_deps(bld, tgt_list):
806     '''save the dependency calculations between builds, to make
807        further builds faster'''
808     denv = Environment.Environment()
809
810     denv.version = savedeps_version
811     denv.savedeps_inputs = savedeps_inputs
812     denv.savedeps_outputs = savedeps_outputs
813     denv.input = {}
814     denv.output = {}
815     denv.outenv = {}
816     denv.caches = {}
817     denv.envvar = {}
818     denv.files  = {}
819
820     for f in savedeps_files:
821         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
822
823     for c in savedeps_caches:
824         denv.caches[c] = LOCAL_CACHE(bld, c)
825
826     for e in savedeps_envvars:
827         denv.envvar[e] = bld.env[e]
828
829     for t in tgt_list:
830         # save all the input attributes for each target
831         tdeps = {}
832         for attr in savedeps_inputs:
833             v = getattr(t, attr, None)
834             if v is not None:
835                 tdeps[attr] = v
836         if tdeps != {}:
837             denv.input[t.sname] = tdeps
838
839         # save all the output attributes for each target
840         tdeps = {}
841         for attr in savedeps_outputs:
842             v = getattr(t, attr, None)
843             if v is not None:
844                 tdeps[attr] = v
845         if tdeps != {}:
846             denv.output[t.sname] = tdeps
847
848         tdeps = {}
849         for attr in savedeps_outenv:
850             if attr in t.env:
851                 tdeps[attr] = t.env[attr]
852         if tdeps != {}:
853             denv.outenv[t.sname] = tdeps
854
855     depsfile = os.path.join(bld.bdir, "sambadeps")
856     denv.store(depsfile)
857
858
859
860 def load_samba_deps(bld, tgt_list):
861     '''load a previous set of build dependencies if possible'''
862     depsfile = os.path.join(bld.bdir, "sambadeps")
863     denv = Environment.Environment()
864     try:
865         debug('deps: checking saved dependencies')
866         denv.load(depsfile)
867         if (denv.version != savedeps_version or
868             denv.savedeps_inputs != savedeps_inputs or
869             denv.savedeps_outputs != savedeps_outputs):
870             return False
871     except:
872         return False
873
874     # check if critical files have changed
875     for f in savedeps_files:
876         if f not in denv.files:
877             return False
878         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
879             return False
880
881     # check if caches are the same
882     for c in savedeps_caches:
883         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
884             return False
885
886     # check if caches are the same
887     for e in savedeps_envvars:
888         if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
889             return False
890
891     # check inputs are the same
892     for t in tgt_list:
893         tdeps = {}
894         for attr in savedeps_inputs:
895             v = getattr(t, attr, None)
896             if v is not None:
897                 tdeps[attr] = v
898         if t.sname in denv.input:
899             olddeps = denv.input[t.sname]
900         else:
901             olddeps = {}
902         if tdeps != olddeps:
903             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
904             return False
905
906     # put outputs in place
907     for t in tgt_list:
908         if not t.sname in denv.output: continue
909         tdeps = denv.output[t.sname]
910         for a in tdeps:
911             setattr(t, a, tdeps[a])
912
913     # put output env vars in place
914     for t in tgt_list:
915         if not t.sname in denv.outenv: continue
916         tdeps = denv.outenv[t.sname]
917         for a in tdeps:
918             t.env[a] = tdeps[a]
919
920     debug('deps: loaded saved dependencies')
921     return True
922
923
924
925 def check_project_rules(bld):
926     '''check the project rules - ensuring the targets are sane'''
927
928     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
929     loops = {}
930     inc_loops = {}
931
932     # build a list of task generators we are interested in
933     tgt_list = []
934     for tgt in targets:
935         type = targets[tgt]
936         if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
937             continue
938         t = bld.name_to_obj(tgt, bld.env)
939         if t is None:
940             Logs.error("Target %s of type %s has no task generator" % (tgt, type))
941             sys.exit(1)
942         tgt_list.append(t)
943
944     add_samba_attributes(bld, tgt_list)
945
946     if load_samba_deps(bld, tgt_list):
947         return
948
949     Logs.info("Checking project rules ...")
950
951     debug('deps: project rules checking started')
952
953     expand_subsystem_deps(bld)
954     build_direct_deps(bld, tgt_list)
955     break_dependency_loops(bld, tgt_list)
956     calculate_final_deps(bld, tgt_list, loops)
957
958     # run the various attribute generators
959     for f in [ build_dependencies, build_includes, add_init_functions ]:
960         debug('deps: project rules checking %s', f)
961         for t in tgt_list: f(t)
962
963     debug('deps: project rules stage1 completed')
964
965     #check_orpaned_targets(bld, tgt_list)
966
967     if not check_duplicate_sources(bld, tgt_list):
968         Logs.error("Duplicate sources present - aborting")
969         sys.exit(1)
970
971     if not check_group_ordering(bld, tgt_list):
972         Logs.error("Bad group ordering - aborting")
973         sys.exit(1)
974
975     show_final_deps(bld, tgt_list)
976
977     debug('deps: project rules checking completed - %u targets checked',
978           len(tgt_list))
979
980     save_samba_deps(bld, tgt_list)
981
982     Logs.info("Project rules pass")
983
984
985 def CHECK_PROJECT_RULES(bld):
986     '''enable checking of project targets for sanity'''
987     if bld.env.added_project_rules:
988         return
989     bld.env.added_project_rules = True
990     bld.add_pre_fun(check_project_rules)
991 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
992
993