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