1 # Samba automatic dependency handling and project rules
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
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)
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
23 def SET_SYSLIB_DEPS(conf, target, deps):
24 '''setup some implied dependencies for a SYSLIB'''
25 cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
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'''
35 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
36 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
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':
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)
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']:
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)
68 def build_dependencies(self):
69 '''This builds the dependency list for a target. It runs after all the targets are declared
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.
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)
80 # extra link flags from pkg_config
81 libs = self.final_syslibs.copy()
83 (ccflags, ldflags) = library_flags(self, list(libs))
84 new_ldflags = getattr(self, 'ldflags', [])
85 new_ldflags.extend(ldflags)
86 self.ldflags = new_ldflags
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)
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)
100 if getattr(self, 'uselib', None):
102 for l in self.uselib:
103 up_list.append(l.upper())
104 self.uselib = up_list
107 def build_includes(self):
108 '''This builds the right set of includes for a target.
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).
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=
121 if getattr(self, 'samba_includes', None) is None:
126 inc_deps = includes_objects(bld, self, set(), {})
130 # maybe add local includes
131 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
134 includes.extend(self.samba_includes_extended)
136 if 'EXTRA_INCLUDES' in bld.env:
137 includes.extend(bld.env['EXTRA_INCLUDES'])
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:
152 tpath = t.samba_abspath
154 npath = tpath + '/' + inc
155 if not npath in inc_set:
156 inc_abs.append(npath)
159 mypath = self.path.abspath(bld.env)
161 relpath = os_path_relpath(inc, mypath)
162 includes.append(relpath)
164 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
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
173 # some are already top based
174 includes_top.append(i)
176 absinc = os.path.join(self.path.abspath(), i)
177 relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
178 includes_top.append('#' + relinc)
180 self.includes = unique_list(includes_top)
181 debug('deps: includes for target %s: includes=%s',
182 self.sname, self.includes)
187 def add_init_functions(self):
188 '''This builds the right set of init functions'''
192 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
194 # cope with the separated object lists from BINARY and LIBRARY targets
196 if sname.endswith('.objlist'):
200 if sname in subsystems:
201 modules.append(sname)
203 m = getattr(self, 'samba_modules', None)
205 modules.extend(TO_LIST(m))
207 m = getattr(self, 'samba_subsystem', None)
211 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
213 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
214 cflags = getattr(self, 'samba_cflags', [])[:]
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
224 bld.ASSERT(m in subsystems,
225 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
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)
235 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
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
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'''
248 debug('deps: checking for duplicate sources')
250 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
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'))
259 for s in source_list:
260 p = os.path.normpath(os.path.join(tpath, s))
262 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
265 t.samba_source_set = obj_sources
269 # build a list of targets that each source file is part of
272 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
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())
278 if not s in subsystems:
280 if not t.sname in subsystems[s]:
281 subsystems[s][t.sname] = []
282 subsystems[s][t.sname].append(t2.sname)
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]))
295 def check_orpaned_targets(bld, tgt_list):
296 '''check if any build targets are orphaned'''
298 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
300 debug('deps: checking for orphaned targets')
303 if getattr(t, 'samba_used', False) == True:
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))
311 def check_group_ordering(bld, tgt_list):
312 '''see if we have any dependencies that violate the group ordering
314 It is an error for a target to depend on a target from a later
319 tm = bld.task_manager
320 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
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
329 for g in bld.task_manager.groups:
334 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
338 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
340 t2 = bld.name_to_obj(d, bld.env)
343 map1 = grp_map[t.samba_group]
344 map2 = grp_map[t2.samba_group]
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))
354 def show_final_deps(bld, tgt_list):
355 '''show the final dependencies for all targets'''
357 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
360 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
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', []))
366 def add_samba_attributes(bld, tgt_list):
367 '''ensure a target has a the required samba attributes'''
369 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
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', '')
382 def replace_grouping_libraries(bld, tgt_list):
383 '''replace dependencies based on grouping libraries
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
390 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
394 # find our list of grouping libraries, mapped from the subsystems they depend on
396 if not getattr(t, 'grouping_library', False):
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
403 # now replace any dependencies on elements of grouping libraries
405 for i in range(len(t.samba_deps_extended)):
406 dep = t.samba_deps_extended[i]
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]
414 def build_direct_deps(bld, tgt_list):
415 '''build the direct_objects and direct_libs sets for each target'''
417 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
418 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
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')
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)
437 if d == t.sname: continue
439 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
441 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
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))
448 if targets[d] == 'SYSLIB':
449 t.direct_syslibs.add(d)
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)
459 Logs.error('Implied dependency %s in %s is of type %s' % (
460 implied, t.sname, targets[implied]))
463 t2 = bld.name_to_obj(d, bld.env)
465 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
467 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
469 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
470 t.direct_objects.add(d)
471 debug('deps: built direct dependencies')
474 def dependency_loop(loops, t, target):
475 '''add a dependency loop to the loops dictionary'''
476 if t.sname == target:
478 if not target in loops:
479 loops[target] = set()
480 if not t.sname in loops[target]:
481 loops[target].add(t.sname)
484 def indirect_libs(bld, t, chain, loops):
485 '''recursively calculate the indirect library dependencies for a target
487 An indirect library is a library that results from a dependency on
491 ret = getattr(t, 'indirect_libs', None)
496 for obj in t.direct_objects:
498 dependency_loop(loops, t, obj)
501 t2 = bld.name_to_obj(obj, bld.env)
502 r2 = indirect_libs(bld, t2, chain, loops)
504 ret = ret.union(t2.direct_libs)
507 for obj in indirect_objects(bld, t, set(), loops):
509 dependency_loop(loops, t, obj)
512 t2 = bld.name_to_obj(obj, bld.env)
513 r2 = indirect_libs(bld, t2, chain, loops)
515 ret = ret.union(t2.direct_libs)
518 t.indirect_libs = ret
523 def indirect_objects(bld, t, chain, loops):
524 '''recursively calculate the indirect object dependencies for a target
526 indirect objects are the set of objects from expanding the
527 subsystem dependencies
530 ret = getattr(t, 'indirect_objects', None)
531 if ret is not None: return ret
534 for lib in t.direct_objects:
536 dependency_loop(loops, t, lib)
539 t2 = bld.name_to_obj(lib, bld.env)
540 r2 = indirect_objects(bld, t2, chain, loops)
542 ret = ret.union(t2.direct_objects)
545 t.indirect_objects = ret
549 def extended_objects(bld, t, chain):
550 '''recursively calculate the extended object dependencies for a target
552 extended objects are the union of:
555 - direct and indirect objects of all direct and indirect libraries
558 ret = getattr(t, 'extended_objects', None)
559 if ret is not None: return ret
562 ret = ret.union(t.final_objects)
564 for lib in t.final_libs:
567 t2 = bld.name_to_obj(lib, bld.env)
569 r2 = extended_objects(bld, t2, chain)
571 ret = ret.union(t2.final_objects)
574 t.extended_objects = ret
578 def includes_objects(bld, t, chain, inc_loops):
579 '''recursively calculate the includes object dependencies for a target
581 includes dependencies come from either library or object dependencies
583 ret = getattr(t, 'includes_objects', None)
587 ret = t.direct_objects.copy()
588 ret = ret.union(t.direct_libs)
590 for obj in t.direct_objects:
592 dependency_loop(inc_loops, t, obj)
595 t2 = bld.name_to_obj(obj, bld.env)
596 r2 = includes_objects(bld, t2, chain, inc_loops)
598 ret = ret.union(t2.direct_objects)
601 for lib in t.direct_libs:
603 dependency_loop(inc_loops, t, lib)
606 t2 = bld.name_to_obj(lib, bld.env)
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))
612 r2 = includes_objects(bld, t2, chain, inc_loops)
614 ret = ret.union(t2.direct_objects)
617 t.includes_objects = ret
621 def break_dependency_loops(bld, tgt_list):
622 '''find and break dependency loops'''
626 # build up the list of loops
628 indirect_objects(bld, t, set(), loops)
629 indirect_libs(bld, t, set(), loops)
630 includes_objects(bld, t, set(), inc_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]))
640 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
642 for loop in inc_loops:
643 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
645 # expand the loops mapping by one level
646 for loop in loops.copy():
647 for tgt in loops[loop]:
649 loops[loop] = loops[loop].union(loops[tgt])
651 for loop in inc_loops.copy():
652 for tgt in inc_loops[loop]:
654 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
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)
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)
676 # add in the replacement dependencies
679 for attr in ['indirect_objects', 'indirect_libs']:
680 objs = getattr(t, attr, set())
682 diff = loops[loop].difference(objs)
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)
690 for loop in inc_loops:
691 objs = getattr(t, 'includes_objects', set())
693 diff = inc_loops[loop].difference(objs)
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)
702 def reduce_objects(bld, tgt_list):
703 '''reduce objects by looking for indirect object dependencies'''
707 t.extended_objects = None
711 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
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])
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)
729 rely_on[l] = rely_on[l].union(dup)
730 t.final_objects = new
735 # add back in any objects that were relied upon by the reduction rules
737 t = bld.name_to_obj(r, bld.env)
738 t.final_objects = t.final_objects.union(rely_on[r])
743 def show_library_loop(bld, lib1, lib2, path, seen):
744 '''show the detailed path of a library loop between lib1 and lib2'''
746 t = bld.name_to_obj(lib1, bld.env)
747 if not lib2 in getattr(t, 'final_libs', set()):
750 for d in t.samba_deps_extended:
754 path2 = path + '=>' + d
756 Logs.warn('library loop path: ' + path2)
758 show_library_loop(bld, d, lib2, path2, seen)
762 def calculate_final_deps(bld, tgt_list, loops):
763 '''calculate the final library and object dependencies'''
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))
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)
776 # handle any non-shared binaries
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')
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)
787 Logs.error('ERROR: subsystem %s not found' % objname)
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
801 t2 = bld.name_to_obj(objname, bld.env)
803 Logs.error('ERROR: subsystem %s not found' % objname)
805 t.final_objects.add(objname)
806 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
809 # find any library loops
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)
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())
830 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
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']:
837 if t.samba_type != type: continue
839 if loop in t.final_libs:
840 diff = loops[loop].difference(t.final_libs)
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)
853 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
855 t.final_libs = t.final_libs.union(diff)
857 # remove objects that are also available in linked libs
859 while reduce_objects(bld, tgt_list):
862 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
864 debug('deps: Object reduction took %u iterations', count)
866 # add in any syslib dependencies
868 if not t.samba_type in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
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
882 # find any unresolved library loops
883 lib_loop_error = False
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
894 debug('deps: removed duplicate dependencies')
897 def show_dependencies(bld, target, seen):
898 '''recursively show the dependencies of target'''
903 t = bld.name_to_obj(target, bld.env)
905 Logs.error("ERROR: Unable to find target '%s'" % target)
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))
914 for t2 in t.direct_objects:
915 show_dependencies(bld, t2, seen)
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'''
922 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
926 Logs.info("showing duplicate objects")
929 if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
931 for n in getattr(t, 'final_objects', set()):
932 t2 = bld.name_to_obj(n, bld.env)
935 used_by[n].add(t.sname)
938 if len(used_by[n]) > 1:
939 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
941 Logs.info("showing indirect dependency counts (sorted by count)")
943 def indirect_count(t1, t2):
944 return len(t2.indirect_objects) - len(t1.indirect_objects)
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)))
952 ######################################################################
953 # this provides a way to save our dependency calculations between runs
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']
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()
967 denv.version = savedeps_version
968 denv.savedeps_inputs = savedeps_inputs
969 denv.savedeps_outputs = savedeps_outputs
977 for f in savedeps_files:
978 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
980 for c in savedeps_caches:
981 denv.caches[c] = LOCAL_CACHE(bld, c)
983 for e in savedeps_envvars:
984 denv.envvar[e] = bld.env[e]
987 # save all the input attributes for each target
989 for attr in savedeps_inputs:
990 v = getattr(t, attr, None)
994 denv.input[t.sname] = tdeps
996 # save all the output attributes for each target
998 for attr in savedeps_outputs:
999 v = getattr(t, attr, None)
1003 denv.output[t.sname] = tdeps
1006 for attr in savedeps_outenv:
1008 tdeps[attr] = t.env[attr]
1010 denv.outenv[t.sname] = tdeps
1012 depsfile = os.path.join(bld.bdir, "sambadeps")
1013 denv.store(depsfile)
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()
1022 debug('deps: checking saved dependencies')
1024 if (denv.version != savedeps_version or
1025 denv.savedeps_inputs != savedeps_inputs or
1026 denv.savedeps_outputs != savedeps_outputs):
1031 # check if critical files have changed
1032 for f in savedeps_files:
1033 if f not in denv.files:
1035 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
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):
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]:
1048 # check inputs are the same
1051 for attr in savedeps_inputs:
1052 v = getattr(t, attr, None)
1055 if t.sname in denv.input:
1056 olddeps = denv.input[t.sname]
1059 if tdeps != olddeps:
1060 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1063 # put outputs in place
1065 if not t.sname in denv.output: continue
1066 tdeps = denv.output[t.sname]
1068 setattr(t, a, tdeps[a])
1070 # put output env vars in place
1072 if not t.sname in denv.outenv: continue
1073 tdeps = denv.outenv[t.sname]
1077 debug('deps: loaded saved dependencies')
1082 def check_project_rules(bld):
1083 '''check the project rules - ensuring the targets are sane'''
1088 tgt_list = get_tgt_list(bld)
1090 add_samba_attributes(bld, tgt_list)
1092 force_project_rules = (Options.options.SHOWDEPS or
1093 Options.options.SHOW_DUPLICATES)
1095 if not force_project_rules and load_samba_deps(bld, tgt_list):
1099 tstart = time.clock()
1101 bld.new_rules = True
1102 Logs.info("Checking project rules ...")
1104 debug('deps: project rules checking started')
1106 expand_subsystem_deps(bld)
1108 debug("deps: expand_subsystem_deps: %f" % (time.clock() - tstart))
1110 replace_grouping_libraries(bld, tgt_list)
1112 debug("deps: replace_grouping_libraries: %f" % (time.clock() - tstart))
1114 build_direct_deps(bld, tgt_list)
1116 debug("deps: build_direct_deps: %f" % (time.clock() - tstart))
1118 break_dependency_loops(bld, tgt_list)
1120 debug("deps: break_dependency_loops: %f" % (time.clock() - tstart))
1122 if Options.options.SHOWDEPS:
1123 show_dependencies(bld, Options.options.SHOWDEPS, set())
1125 calculate_final_deps(bld, tgt_list, loops)
1127 debug("deps: calculate_final_deps: %f" % (time.clock() - tstart))
1129 if Options.options.SHOW_DUPLICATES:
1130 show_object_duplicates(bld, tgt_list)
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))
1138 debug('deps: project rules stage1 completed')
1140 #check_orpaned_targets(bld, tgt_list)
1142 if not check_duplicate_sources(bld, tgt_list):
1143 Logs.error("Duplicate sources present - aborting")
1146 debug("deps: check_duplicate_sources: %f" % (time.clock() - tstart))
1148 if not check_group_ordering(bld, tgt_list):
1149 Logs.error("Bad group ordering - aborting")
1152 debug("deps: check_group_ordering: %f" % (time.clock() - tstart))
1154 show_final_deps(bld, tgt_list)
1156 debug("deps: show_final_deps: %f" % (time.clock() - tstart))
1158 debug('deps: project rules checking completed - %u targets checked',
1161 if not bld.is_install:
1162 save_samba_deps(bld, tgt_list)
1164 debug("deps: save_samba_deps: %f" % (time.clock() - tstart))
1166 Logs.info("Project rules pass")
1169 def CHECK_PROJECT_RULES(bld):
1170 '''enable checking of project targets for sanity'''
1171 if bld.env.added_project_rules:
1173 bld.env.added_project_rules = True
1174 bld.add_pre_fun(check_project_rules)
1175 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES