1 # Samba automatic dependency handling and project rules
3 import Build, os, re, Environment, Logs
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
22 def TARGET_ALIAS(bld, target, alias):
23 '''define an alias for a target name'''
24 cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
26 Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
29 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
33 def SET_SYSLIB_DEPS(conf, target, deps):
34 '''setup some implied dependencies for a SYSLIB'''
35 cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
39 def EXPAND_ALIAS(bld, target):
40 '''expand a target name via an alias'''
41 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
43 return aliases[target]
45 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
48 def expand_subsystem_deps(bld):
49 '''expand the reverse dependencies resulting from subsystem
50 attributes of modules. This is walking over the complete list
51 of declared subsystems, and expands the samba_deps_extended list for any
52 module<->subsystem dependencies'''
54 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
55 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
56 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
58 for subsystem_name in subsystem_list:
59 if subsystem_name in aliases:
60 subsystem_name = aliases[subsystem_name]
61 bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name)
62 type = targets[subsystem_name]
63 if type == 'DISABLED' or type == 'EMPTY':
67 # subsystem_name = dcerpc_server (a subsystem)
68 # subsystem = dcerpc_server (a subsystem object)
69 # module_name = rpc_epmapper (a module within the dcerpc_server subsystem)
70 # module = rpc_epmapper (a module object within the dcerpc_server subsystem)
72 subsystem = bld.name_to_obj(subsystem_name, bld.env)
73 for d in subsystem_list[subsystem_name]:
74 module_name = d['TARGET']
75 module_type = targets[module_name]
76 if module_type in ['DISABLED', 'EMPTY']:
78 bld.ASSERT(subsystem is not None,
79 "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type))
80 if module_type in ['SUBSYSTEM']:
81 # if a module is a plain object type (not a library) then the
82 # subsystem it is part of needs to have it as a dependency, so targets
83 # that depend on this subsystem get the modules of that subsystem
84 subsystem.samba_deps_extended.append(module_name)
85 module = bld.name_to_obj(module_name, bld.env)
86 module.samba_includes_extended.extend(subsystem.samba_includes_extended)
87 if targets[subsystem_name] in ['SUBSYSTEM']:
88 # if a subsystem is a plain object type (not a library) then any modules
89 # in that subsystem need to depend on the subsystem
90 module.samba_deps_extended.extend(subsystem.samba_deps_extended)
91 subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended)
95 def build_dependencies(self):
96 '''This builds the dependency list for a target. It runs after all the targets are declared
98 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
99 the full dependency list for a target until we have all of the targets declared.
102 if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
103 self.uselib = list(self.final_syslibs)
104 self.uselib_local = list(self.final_libs)
105 self.add_objects = list(self.final_objects)
107 # extra link flags from pkg_config
108 libs = self.final_syslibs.copy()
110 (ccflags, ldflags) = library_flags(self, list(libs))
111 new_ldflags = getattr(self, 'ldflags', [])
112 new_ldflags.extend(ldflags)
113 self.ldflags = new_ldflags
115 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
116 self.sname, self.uselib, self.uselib_local, self.add_objects)
118 if self.samba_type in ['SUBSYSTEM']:
119 # this is needed for the ccflags of libs that come from pkg_config
120 self.uselib = list(self.direct_syslibs)
122 if getattr(self, 'uselib', None):
124 for l in self.uselib:
125 up_list.append(l.upper())
126 self.uselib = up_list
128 def build_includes(self):
129 '''This builds the right set of includes for a target.
131 One tricky part of this is that the includes= attribute for a
132 target needs to use paths which are relative to that targets
133 declaration directory (which we can get at via t.path).
135 The way this works is the includes list gets added as
136 samba_includes in the main build task declaration. Then this
137 function runs after all of the tasks are declared, and it
138 processes the samba_includes attribute to produce a includes=
142 if getattr(self, 'samba_includes', None) is None:
147 inc_deps = includes_objects(bld, self, set(), {})
151 # maybe add local includes
152 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
155 includes.extend(self.samba_includes_extended)
157 if 'EXTRA_INCLUDES' in bld.env:
158 includes.extend(bld.env['EXTRA_INCLUDES'])
166 t = bld.name_to_obj(d, bld.env)
167 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
168 inclist = getattr(t, 'samba_includes_extended', [])
169 if getattr(t, 'local_include', True) == True:
173 tpath = t.samba_abspath
175 npath = tpath + '/' + inc
176 if not npath in inc_set:
177 inc_abs.append(npath)
180 mypath = self.path.abspath(bld.env)
182 relpath = os_path_relpath(inc, mypath)
183 includes.append(relpath)
185 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
188 # now transform the includes list to be relative to the top directory
189 # which is represented by '#' in waf. This allows waf to cache the
190 # includes lists more efficiently
194 # some are already top based
195 includes_top.append(i)
197 absinc = os.path.join(self.path.abspath(), i)
198 relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
199 includes_top.append('#' + relinc)
201 self.includes = unique_list(includes_top)
202 debug('deps: includes for target %s: includes=%s',
203 self.sname, self.includes)
208 def add_init_functions(self):
209 '''This builds the right set of init functions'''
213 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
215 # cope with the separated object lists from BINARY and LIBRARY targets
217 if sname.endswith('.objlist'):
221 if sname in subsystems:
222 modules.append(sname)
224 m = getattr(self, 'samba_modules', None)
226 modules.extend(TO_LIST(m))
228 m = getattr(self, 'samba_subsystem', None)
235 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
237 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
239 cflags = getattr(self, 'samba_cflags', [])[:]
241 bld.ASSERT(m in subsystems,
242 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
244 for d in subsystems[m]:
245 if targets[d['TARGET']] != 'DISABLED':
246 init_fn_list.append(d['INIT_FUNCTION'])
247 if init_fn_list == []:
248 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
250 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
251 self.ccflags = cflags
255 def check_duplicate_sources(bld, tgt_list):
256 '''see if we are compiling the same source file into multiple
257 subsystem targets for the same library or binary'''
259 debug('deps: checking for duplicate sources')
261 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
267 obj_sources = getattr(t, 'source', '')
268 tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
269 obj_sources = bld.SUBDIR(tpath, obj_sources)
270 t.samba_source_set = set(TO_LIST(obj_sources))
273 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
277 for obj in t.add_objects:
278 t2 = t.bld.name_to_obj(obj, bld.env)
279 source_set = getattr(t2, 'samba_source_set', set())
280 sources.append( { 'dep':obj, 'src':source_set} )
283 if s['dep'] == s2['dep']: continue
284 common = s['src'].intersection(s2['src'])
285 if common.difference(seen):
286 Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
289 seen = seen.union(common)
294 def check_orpaned_targets(bld, tgt_list):
295 '''check if any build targets are orphaned'''
297 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
299 debug('deps: checking for orphaned targets')
302 if getattr(t, 'samba_used', False) == True:
304 type = target_dict[t.sname]
305 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
306 if re.search('^PIDL_', t.sname) is None:
307 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
310 def check_group_ordering(bld, tgt_list):
311 '''see if we have any dependencies that violate the group ordering
313 It is an error for a target to depend on a target from a later
318 tm = bld.task_manager
319 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
321 for g in bld.task_manager.groups:
322 gname = group_name(g)
323 for t in g.tasks_gen:
324 t.samba_group = gname
328 for g in bld.task_manager.groups:
333 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
337 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
339 t2 = bld.name_to_obj(d, bld.env)
342 map1 = grp_map[t.samba_group]
343 map2 = grp_map[t2.samba_group]
346 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
347 t.sname, t.samba_group, t2.sname, t2.samba_group))
353 def show_final_deps(bld, tgt_list):
354 '''show the final dependencies for all targets'''
356 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
359 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
361 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
362 t.sname, t.uselib, t.uselib_local, t.add_objects)
365 def add_samba_attributes(bld, tgt_list):
366 '''ensure a target has a the required samba attributes'''
368 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
375 t.samba_type = targets[t.sname]
376 t.samba_abspath = t.path.abspath(bld.env)
377 t.samba_deps_extended = t.samba_deps[:]
378 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
379 t.ccflags = getattr(t, 'samba_cflags', '')
381 def replace_grouping_libraries(bld, tgt_list):
382 '''replace dependencies based on grouping libraries
384 If a library is marked as a grouping library, then any target that
385 depends on a subsystem that is part of that grouping library gets
386 that dependency replaced with a dependency on the grouping library
389 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
393 # find our list of grouping libraries, mapped from the subsystems they depend on
395 if not getattr(t, 'grouping_library', False):
397 for dep in t.samba_deps_extended:
398 if targets[dep] == 'SUBSYSTEM':
399 grouping[dep] = t.sname
401 # now replace any dependencies on elements of grouping libraries
403 for i in range(len(t.samba_deps_extended)):
404 dep = t.samba_deps_extended[i]
406 if t.sname != grouping[dep]:
407 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
408 t.samba_deps_extended[i] = grouping[dep]
412 def build_direct_deps(bld, tgt_list):
413 '''build the direct_objects and direct_libs sets for each target'''
415 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
416 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
418 global_deps = bld.env.GLOBAL_DEPENDENCIES
419 global_deps_exclude = set()
420 for dep in global_deps:
421 t = bld.name_to_obj(dep, bld.env)
422 for d in t.samba_deps:
423 # prevent loops from the global dependencies list
424 global_deps_exclude.add(d)
425 global_deps_exclude.add(d + '.objlist')
428 t.direct_objects = set()
429 t.direct_libs = set()
430 t.direct_syslibs = set()
431 deps = t.samba_deps_extended[:]
432 if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
433 deps.extend(global_deps)
435 d = EXPAND_ALIAS(bld, d)
436 if d == t.sname: continue
438 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
440 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
442 if targets[d] == 'SYSLIB':
443 t.direct_syslibs.add(d)
445 for implied in TO_LIST(syslib_deps[d]):
446 if BUILTIN_LIBRARY(bld, implied):
447 t.direct_objects.add(implied)
448 elif targets[implied] == 'SYSLIB':
449 t.direct_syslibs.add(implied)
450 elif targets[implied] in ['LIBRARY', 'MODULE']:
451 t.direct_libs.add(implied)
453 Logs.error('Implied dependency %s in %s is of type %s' % (
454 implied, t.sname, targets[implied]))
457 t2 = bld.name_to_obj(d, bld.env)
459 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
461 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
463 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
464 t.direct_objects.add(d)
465 debug('deps: built direct dependencies')
468 def dependency_loop(loops, t, target):
469 '''add a dependency loop to the loops dictionary'''
470 if t.sname == target:
472 if not target in loops:
473 loops[target] = set()
474 if not t.sname in loops[target]:
475 loops[target].add(t.sname)
478 def indirect_libs(bld, t, chain, loops):
479 '''recursively calculate the indirect library dependencies for a target
481 An indirect library is a library that results from a dependency on
485 ret = getattr(t, 'indirect_libs', None)
490 for obj in t.direct_objects:
492 dependency_loop(loops, t, obj)
495 t2 = bld.name_to_obj(obj, bld.env)
496 r2 = indirect_libs(bld, t2, chain, loops)
498 ret = ret.union(t2.direct_libs)
501 for obj in indirect_objects(bld, t, set(), loops):
503 dependency_loop(loops, t, obj)
506 t2 = bld.name_to_obj(obj, bld.env)
507 r2 = indirect_libs(bld, t2, chain, loops)
509 ret = ret.union(t2.direct_libs)
512 t.indirect_libs = ret
517 def indirect_objects(bld, t, chain, loops):
518 '''recursively calculate the indirect object dependencies for a target
520 indirect objects are the set of objects from expanding the
521 subsystem dependencies
524 ret = getattr(t, 'indirect_objects', None)
525 if ret is not None: return ret
528 for lib in t.direct_objects:
530 dependency_loop(loops, t, lib)
533 t2 = bld.name_to_obj(lib, bld.env)
534 r2 = indirect_objects(bld, t2, chain, loops)
536 ret = ret.union(t2.direct_objects)
539 t.indirect_objects = ret
543 def extended_objects(bld, t, chain):
544 '''recursively calculate the extended object dependencies for a target
546 extended objects are the union of:
549 - direct and indirect objects of all direct and indirect libraries
552 ret = getattr(t, 'extended_objects', None)
553 if ret is not None: return ret
556 ret = ret.union(t.final_objects)
558 for lib in t.final_libs:
561 t2 = bld.name_to_obj(lib, bld.env)
563 r2 = extended_objects(bld, t2, chain)
565 ret = ret.union(t2.final_objects)
568 t.extended_objects = ret
572 def includes_objects(bld, t, chain, inc_loops):
573 '''recursively calculate the includes object dependencies for a target
575 includes dependencies come from either library or object dependencies
577 ret = getattr(t, 'includes_objects', None)
581 ret = t.direct_objects.copy()
582 ret = ret.union(t.direct_libs)
584 for obj in t.direct_objects:
586 dependency_loop(inc_loops, t, obj)
589 t2 = bld.name_to_obj(obj, bld.env)
590 r2 = includes_objects(bld, t2, chain, inc_loops)
592 ret = ret.union(t2.direct_objects)
595 for lib in t.direct_libs:
597 dependency_loop(inc_loops, t, lib)
600 t2 = bld.name_to_obj(lib, bld.env)
602 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
603 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
604 lib, targets[lib], t.sname))
606 r2 = includes_objects(bld, t2, chain, inc_loops)
608 ret = ret.union(t2.direct_objects)
611 t.includes_objects = ret
615 def break_dependency_loops(bld, tgt_list):
616 '''find and break dependency loops'''
620 # build up the list of loops
622 indirect_objects(bld, t, set(), loops)
623 indirect_libs(bld, t, set(), loops)
624 includes_objects(bld, t, set(), inc_loops)
629 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
630 objs = getattr(t, attr, set())
631 setattr(t, attr, objs.difference(loops[t.sname]))
634 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
636 for loop in inc_loops:
637 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
639 # expand the loops mapping by one level
640 for loop in loops.copy():
641 for tgt in loops[loop]:
643 loops[loop] = loops[loop].union(loops[tgt])
645 for loop in inc_loops.copy():
646 for tgt in inc_loops[loop]:
648 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
651 # expand indirect subsystem and library loops
652 for loop in loops.copy():
653 t = bld.name_to_obj(loop, bld.env)
654 if t.samba_type in ['SUBSYSTEM']:
655 loops[loop] = loops[loop].union(t.indirect_objects)
656 loops[loop] = loops[loop].union(t.direct_objects)
657 if t.samba_type in ['LIBRARY','PYTHON']:
658 loops[loop] = loops[loop].union(t.indirect_libs)
659 loops[loop] = loops[loop].union(t.direct_libs)
660 if loop in loops[loop]:
661 loops[loop].remove(loop)
663 # expand indirect includes loops
664 for loop in inc_loops.copy():
665 t = bld.name_to_obj(loop, bld.env)
666 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
667 if loop in inc_loops[loop]:
668 inc_loops[loop].remove(loop)
670 # add in the replacement dependencies
673 for attr in ['indirect_objects', 'indirect_libs']:
674 objs = getattr(t, attr, set())
676 diff = loops[loop].difference(objs)
680 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
681 objs = objs.union(diff)
682 setattr(t, attr, objs)
684 for loop in inc_loops:
685 objs = getattr(t, 'includes_objects', set())
687 diff = inc_loops[loop].difference(objs)
691 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
692 objs = objs.union(diff)
693 setattr(t, 'includes_objects', objs)
696 def reduce_objects(bld, tgt_list):
697 '''reduce objects by looking for indirect object dependencies'''
701 t.extended_objects = None
705 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
707 if t.samba_type != type: continue
708 # if we will indirectly link to a target then we don't need it
709 new = t.final_objects.copy()
710 for l in t.final_libs:
711 t2 = bld.name_to_obj(l, bld.env)
712 t2_obj = extended_objects(bld, t2, set())
713 dup = new.intersection(t2_obj)
714 if t.sname in rely_on:
715 dup = dup.difference(rely_on[t.sname])
717 debug('deps: removing dups from %s of type %s: %s also in %s %s',
718 t.sname, t.samba_type, dup, t2.samba_type, l)
719 new = new.difference(dup)
723 rely_on[l] = rely_on[l].union(dup)
724 t.final_objects = new
729 # add back in any objects that were relied upon by the reduction rules
731 t = bld.name_to_obj(r, bld.env)
732 t.final_objects = t.final_objects.union(rely_on[r])
737 def calculate_final_deps(bld, tgt_list, loops):
738 '''calculate the final library and object dependencies'''
740 # start with the maximum possible list
741 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
742 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
745 # don't depend on ourselves
746 if t.sname in t.final_libs:
747 t.final_libs.remove(t.sname)
748 if t.sname in t.final_objects:
749 t.final_objects.remove(t.sname)
751 # handle any non-shared binaries
753 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
754 # replace lib deps with objlist deps
755 for l in t.final_libs:
756 objname = l + '.objlist'
757 t2 = bld.name_to_obj(objname, bld.env)
759 Logs.error('ERROR: subsystem %s not found' % objname)
761 t.final_objects.add(objname)
762 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
765 # find any library loops
767 if t.samba_type in ['LIBRARY', 'PYTHON']:
768 for l in t.final_libs.copy():
769 t2 = bld.name_to_obj(l, bld.env)
770 if t.sname in t2.final_libs:
771 if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
772 # we could break this in either direction. If one of the libraries
773 # has a version number, and will this be distributed publicly, then
774 # we should make it the lower level library in the DAG
775 Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
776 dependency_loop(loops, t, t2.sname)
777 t2.final_libs.remove(t.sname)
779 Logs.error('ERROR: circular library dependency between %s and %s'
780 % (t.sname, t2.sname))
784 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
786 # we now need to make corrections for any library loops we broke up
787 # any target that depended on the target of the loop and doesn't
788 # depend on the source of the loop needs to get the loop source added
789 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
791 if t.samba_type != type: continue
793 if loop in t.final_libs:
794 diff = loops[loop].difference(t.final_libs)
799 # make sure we don't recreate the loop again!
800 for d in diff.copy():
801 t2 = bld.name_to_obj(d, bld.env)
802 if t2.samba_type == 'LIBRARY':
803 if t.sname in t2.final_libs:
804 debug('deps: removing expansion %s from %s', d, t.sname)
807 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
809 t.final_libs = t.final_libs.union(diff)
811 # remove objects that are also available in linked libs
813 while reduce_objects(bld, tgt_list):
816 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
818 debug('deps: Object reduction took %u iterations', count)
820 # add in any syslib dependencies
822 if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
825 for d in t.final_objects:
826 t2 = bld.name_to_obj(d, bld.env)
827 syslibs = syslibs.union(t2.direct_syslibs)
828 # this adds the indirect syslibs as well, which may not be needed
829 # depending on the linker flags
830 for d in t.final_libs:
831 t2 = bld.name_to_obj(d, bld.env)
832 syslibs = syslibs.union(t2.direct_syslibs)
833 t.final_syslibs = syslibs
836 # find any unresolved library loops
837 lib_loop_error = False
839 if t.samba_type in ['LIBRARY', 'PYTHON']:
840 for l in t.final_libs.copy():
841 t2 = bld.name_to_obj(l, bld.env)
842 if t.sname in t2.final_libs:
843 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
844 lib_loop_error = True
848 debug('deps: removed duplicate dependencies')
851 def show_dependencies(bld, target, seen):
852 '''recursively show the dependencies of target'''
857 t = bld.name_to_obj(target, bld.env)
859 Logs.error("ERROR: Unable to find target '%s'" % target)
862 Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
863 Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
864 Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
868 for t2 in t.direct_objects:
869 show_dependencies(bld, t2, seen)
872 def show_object_duplicates(bld, tgt_list):
873 '''show a list of object files that are included in more than
874 one library or binary'''
876 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
880 Logs.info("showing duplicate objects")
883 if not targets[t.sname] in [ 'LIBRARY' ]:
885 for n in getattr(t, 'final_objects', set()):
886 t2 = bld.name_to_obj(n, bld.env)
889 used_by[n].add(t.sname)
892 if len(used_by[n]) > 1:
893 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
895 Logs.info("showing indirect dependency counts (sorted by count)")
897 def indirect_count(t1, t2):
898 return len(t2.indirect_objects) - len(t1.indirect_objects)
900 sorted_list = sorted(tgt_list, cmp=indirect_count)
901 for t in sorted_list:
902 if len(t.indirect_objects) > 1:
903 Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
906 ######################################################################
907 # this provides a way to save our dependency calculations between runs
909 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source', 'grouping_library']
910 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
911 savedeps_outenv = ['INC_PATHS']
912 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES']
913 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
914 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
916 def save_samba_deps(bld, tgt_list):
917 '''save the dependency calculations between builds, to make
918 further builds faster'''
919 denv = Environment.Environment()
921 denv.version = savedeps_version
922 denv.savedeps_inputs = savedeps_inputs
923 denv.savedeps_outputs = savedeps_outputs
931 for f in savedeps_files:
932 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
934 for c in savedeps_caches:
935 denv.caches[c] = LOCAL_CACHE(bld, c)
937 for e in savedeps_envvars:
938 denv.envvar[e] = bld.env[e]
941 # save all the input attributes for each target
943 for attr in savedeps_inputs:
944 v = getattr(t, attr, None)
948 denv.input[t.sname] = tdeps
950 # save all the output attributes for each target
952 for attr in savedeps_outputs:
953 v = getattr(t, attr, None)
957 denv.output[t.sname] = tdeps
960 for attr in savedeps_outenv:
962 tdeps[attr] = t.env[attr]
964 denv.outenv[t.sname] = tdeps
966 depsfile = os.path.join(bld.bdir, "sambadeps")
971 def load_samba_deps(bld, tgt_list):
972 '''load a previous set of build dependencies if possible'''
973 depsfile = os.path.join(bld.bdir, "sambadeps")
974 denv = Environment.Environment()
976 debug('deps: checking saved dependencies')
978 if (denv.version != savedeps_version or
979 denv.savedeps_inputs != savedeps_inputs or
980 denv.savedeps_outputs != savedeps_outputs):
985 # check if critical files have changed
986 for f in savedeps_files:
987 if f not in denv.files:
989 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
992 # check if caches are the same
993 for c in savedeps_caches:
994 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
997 # check if caches are the same
998 for e in savedeps_envvars:
999 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1002 # check inputs are the same
1005 for attr in savedeps_inputs:
1006 v = getattr(t, attr, None)
1009 if t.sname in denv.input:
1010 olddeps = denv.input[t.sname]
1013 if tdeps != olddeps:
1014 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1017 # put outputs in place
1019 if not t.sname in denv.output: continue
1020 tdeps = denv.output[t.sname]
1022 setattr(t, a, tdeps[a])
1024 # put output env vars in place
1026 if not t.sname in denv.outenv: continue
1027 tdeps = denv.outenv[t.sname]
1031 debug('deps: loaded saved dependencies')
1036 def check_project_rules(bld):
1037 '''check the project rules - ensuring the targets are sane'''
1039 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1043 # build a list of task generators we are interested in
1047 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
1049 t = bld.name_to_obj(tgt, bld.env)
1051 Logs.error("Target %s of type %s has no task generator" % (tgt, type))
1055 add_samba_attributes(bld, tgt_list)
1057 force_project_rules = (Options.options.SHOWDEPS or
1058 Options.options.SHOW_DUPLICATES)
1060 if not force_project_rules and load_samba_deps(bld, tgt_list):
1063 bld.new_rules = True
1064 Logs.info("Checking project rules ...")
1066 debug('deps: project rules checking started')
1068 expand_subsystem_deps(bld)
1069 replace_grouping_libraries(bld, tgt_list)
1070 build_direct_deps(bld, tgt_list)
1072 break_dependency_loops(bld, tgt_list)
1073 calculate_final_deps(bld, tgt_list, loops)
1075 if Options.options.SHOWDEPS:
1076 show_dependencies(bld, Options.options.SHOWDEPS, set())
1078 if Options.options.SHOW_DUPLICATES:
1079 show_object_duplicates(bld, tgt_list)
1081 # run the various attribute generators
1082 for f in [ build_dependencies, build_includes, add_init_functions ]:
1083 debug('deps: project rules checking %s', f)
1084 for t in tgt_list: f(t)
1086 debug('deps: project rules stage1 completed')
1088 #check_orpaned_targets(bld, tgt_list)
1090 if not check_duplicate_sources(bld, tgt_list):
1091 Logs.error("Duplicate sources present - aborting")
1094 if not check_group_ordering(bld, tgt_list):
1095 Logs.error("Bad group ordering - aborting")
1098 show_final_deps(bld, tgt_list)
1100 debug('deps: project rules checking completed - %u targets checked',
1103 if not bld.is_install:
1104 save_samba_deps(bld, tgt_list)
1106 Logs.info("Project rules pass")
1109 def CHECK_PROJECT_RULES(bld):
1110 '''enable checking of project targets for sanity'''
1111 if bld.env.added_project_rules:
1113 bld.env.added_project_rules = True
1114 bld.add_pre_fun(check_project_rules)
1115 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES