From c5d0208aa90d530270ce4a14d5bcc130ba6ab8e2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 22 Mar 2010 16:57:44 +1100 Subject: build: fixed handling of full dependencies for --disable-shared the subsystem dependency loops get quite complex when shared libs are disabled --- buildtools/wafsamba/samba_deps.py | 256 ++++++++++++++++++++------------------ 1 file changed, 137 insertions(+), 119 deletions(-) (limited to 'buildtools/wafsamba') diff --git a/buildtools/wafsamba/samba_deps.py b/buildtools/wafsamba/samba_deps.py index 1c9bcb75d9..533d68b54e 100644 --- a/buildtools/wafsamba/samba_deps.py +++ b/buildtools/wafsamba/samba_deps.py @@ -107,7 +107,7 @@ def build_includes(self): bld = self.bld - inc_deps = self.includes_objects + inc_deps = includes_objects(bld, self, set(), {}) includes = [] @@ -312,6 +312,7 @@ def add_samba_attributes(bld, tgt_list): t.samba_includes_extended = TO_LIST(t.samba_includes)[:] t.ccflags = getattr(t, 'samba_cflags', '') + def build_direct_deps(bld, tgt_list): '''build the direct_objects and direct_libs sets for each target''' @@ -326,6 +327,7 @@ def build_direct_deps(bld, tgt_list): deps.extend(global_deps) for d in deps: d = EXPAND_ALIAS(bld, d) + if d == t.sname: continue if not d in targets: print "Unknown dependency %s in %s" % (d, t.sname) raise @@ -344,8 +346,17 @@ def build_direct_deps(bld, tgt_list): debug('deps: built direct dependencies') +def dependency_loop(loops, t, target): + '''add a dependency loop to the loops dictionary''' + if t.sname == target: + return + if not target in loops: + loops[target] = set() + if not t.sname in loops[target]: + loops[target].add(t.sname) + -def indirect_libs(bld, t, chain): +def indirect_libs(bld, t, chain, loops): '''recursively calculate the indirect library dependencies for a target An indirect library is a library that results from a dependency on @@ -359,20 +370,22 @@ def indirect_libs(bld, t, chain): ret = set() for obj in t.direct_objects: if obj in chain: + dependency_loop(loops, t, obj) continue chain.add(obj) t2 = bld.name_to_obj(obj, bld.env) - r2 = indirect_libs(bld, t2, chain) + r2 = indirect_libs(bld, t2, chain, loops) chain.remove(obj) ret = ret.union(t2.direct_libs) ret = ret.union(r2) - for obj in t.indirect_objects: + for obj in indirect_objects(bld, t, set(), loops): if obj in chain: + dependency_loop(loops, t, obj) continue chain.add(obj) t2 = bld.name_to_obj(obj, bld.env) - r2 = indirect_libs(bld, t2, chain) + r2 = indirect_libs(bld, t2, chain, loops) chain.remove(obj) ret = ret.union(t2.direct_libs) ret = ret.union(r2) @@ -382,7 +395,7 @@ def indirect_libs(bld, t, chain): return ret -def indirect_syslibs(bld, t, chain): +def indirect_syslibs(bld, t, chain, loops): '''recursively calculate the indirect system library dependencies for a target An indirect syslib results from a subsystem dependency @@ -391,13 +404,15 @@ def indirect_syslibs(bld, t, chain): ret = getattr(t, 'indirect_syslibs', None) if ret is not None: return ret + ret = set() for obj in t.direct_objects: if obj in chain: + dependency_loop(loops, t, obj) continue chain.add(obj) t2 = bld.name_to_obj(obj, bld.env) - r2 = indirect_syslibs(bld, t2, chain) + r2 = indirect_syslibs(bld, t2, chain, loops) chain.remove(obj) ret = ret.union(t2.direct_syslibs) ret = ret.union(r2) @@ -406,7 +421,7 @@ def indirect_syslibs(bld, t, chain): return ret -def indirect_objects(bld, t, chain): +def indirect_objects(bld, t, chain, loops): '''recursively calculate the indirect object dependencies for a target indirect objects are the set of objects from expanding the @@ -419,10 +434,11 @@ def indirect_objects(bld, t, chain): ret = set() for lib in t.direct_objects: if lib in chain: + dependency_loop(loops, t, lib) continue chain.add(lib) t2 = bld.name_to_obj(lib, bld.env) - r2 = indirect_objects(bld, t2, chain) + r2 = indirect_objects(bld, t2, chain, loops) chain.remove(lib) ret = ret.union(t2.direct_objects) ret = ret.union(r2) @@ -431,71 +447,38 @@ def indirect_objects(bld, t, chain): return ret -def expanded_targets(bld, t, chain): - '''recursively calculate the expanded targets for a target +def extended_objects(bld, t, chain): + '''recursively calculate the extended object dependencies for a target - expanded objects are the set of objects, libraries and syslibs - from expanding the subsystem dependencies, library dependencies - and syslib dependencies + extended objects are the union of: + - direct objects + - indirect objects + - direct and indirect objects of all direct and indirect libraries ''' - ret = getattr(t, 'expanded_targets', None) + ret = getattr(t, 'extended_objects', None) if ret is not None: return ret - ret = t.direct_objects.copy() - ret = ret.union(t.direct_libs) - ret = ret.union(t.direct_syslibs) - - direct = ret.copy() + ret = set() + ret = ret.union(t.direct_objects) + ret = ret.union(t.indirect_objects) - for d in direct: - if d in chain: continue - chain.add(d) - t2 = bld.name_to_obj(d, bld.env) - if t2 is None: continue - r2 = expanded_targets(bld, t2, chain) - chain.remove(d) + for lib in t.direct_libs: + if lib in chain: + continue + t2 = bld.name_to_obj(lib, bld.env) + chain.add(lib) + r2 = extended_objects(bld, t2, chain) + chain.remove(lib) + ret = ret.union(t2.direct_objects) + ret = ret.union(t2.indirect_objects) ret = ret.union(r2) - if t.sname in ret: - ret.remove(t.sname) - - t.expanded_targets = ret + t.extended_objects = ret return ret -def expanded_targets2(bld, t, chain): - '''recursively calculate the expanded targets for a target - - expanded objects are the set of objects from expanding the - subsystem dependencies and library dependencies - ''' - - ret = getattr(t, 'expanded_targets2', None) - if ret is not None: return ret - - ret = t.final_objects.copy() - - for attr in [ 'final_objects', 'final_libs' ]: - f = getattr(t, attr, set()) - for d in f.copy(): - if d in chain: - continue - chain.add(d) - t2 = bld.name_to_obj(d, bld.env) - if t2 is None: continue - r2 = expanded_targets2(bld, t2, chain) - chain.remove(d) - ret = ret.union(r2) - - if t.sname in ret: - ret.remove(t.sname) - - t.expanded_targets2 = ret - return ret - - -def includes_objects(bld, t, chain): +def includes_objects(bld, t, chain, inc_loops): '''recursively calculate the includes object dependencies for a target includes dependencies come from either library or object dependencies @@ -509,20 +492,22 @@ def includes_objects(bld, t, chain): for obj in t.direct_objects: if obj in chain: + dependency_loop(inc_loops, t, obj) continue chain.add(obj) t2 = bld.name_to_obj(obj, bld.env) - r2 = includes_objects(bld, t2, chain) + r2 = includes_objects(bld, t2, chain, inc_loops) chain.remove(obj) ret = ret.union(t2.direct_objects) ret = ret.union(r2) for lib in t.direct_libs: if lib in chain: + dependency_loop(inc_loops, t, lib) continue chain.add(lib) t2 = bld.name_to_obj(lib, bld.env) - r2 = includes_objects(bld, t2, chain) + r2 = includes_objects(bld, t2, chain, inc_loops) chain.remove(lib) ret = ret.union(t2.direct_objects) ret = ret.union(r2) @@ -531,35 +516,69 @@ def includes_objects(bld, t, chain): return ret -def build_indirect_deps(bld, tgt_list): - '''build the indirect_objects and indirect_libs sets for each target''' - for t in tgt_list: - indirect_objects(bld, t, set()) - indirect_libs(bld, t, set()) - indirect_syslibs(bld, t, set()) - includes_objects(bld, t, set()) - expanded_targets(bld, t, set()) - debug('deps: built indirect dependencies') +def break_dependency_loops(bld, tgt_list): + '''find and break dependency loops''' + loops = {} + inc_loops = {} + # build up the list of loops + for t in tgt_list: + indirect_objects(bld, t, set(), loops) + indirect_libs(bld, t, set(), loops) + indirect_syslibs(bld, t, set(), loops) + includes_objects(bld, t, set(), inc_loops) -def re_expand2(bld, tgt_list): + # break the loops for t in tgt_list: - t.expanded_targets2 = None - for type in ['BINARY','LIBRARY','PYTHON']: - for t in tgt_list: - if t.samba_type == type: - expanded_targets2(bld, t, set()) + if t.sname in loops: + for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']: + objs = getattr(t, attr, set()) + setattr(t, attr, objs.difference(loops[t.sname])) + + for loop in loops: + debug('deps: Found dependency loops for target %s : %s', loop, loops[loop]) + + # expand the loops mapping by one level + for loop in loops.copy(): + for tgt in loops[loop]: + if tgt in loops: + loops[loop] = loops[loop].union(loops[tgt]) + + # expand indirect subsystem and library loops + for loop in loops.copy(): + t = bld.name_to_obj(loop, bld.env) + if t.samba_type in ['SUBSYSTEM']: + loops[loop] = loops[loop].union(t.indirect_objects, t.direct_objects) + if t.samba_type in ['LIBRARY']: + loops[loop] = loops[loop].union(t.indirect_libs, t.direct_libs) + if loop in loops[loop]: + loops[loop].remove(loop) + + # add in the replacement dependencies for t in tgt_list: - expanded_targets2(bld, t, set()) - - -def calculate_final_deps(bld, tgt_list): + for loop in loops: + for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']: + objs = getattr(t, attr, set()) + if loop in objs: + diff = loops[loop].difference(objs) + if t.sname in diff: + diff.remove(t.sname) + if diff: + debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff) + objs = objs.union(diff) + if t.sname == 'ldb_password_hash': + debug('deps: setting %s %s to %s', t.sname, attr, objs) + setattr(t, attr, objs) + + + +def calculate_final_deps(bld, tgt_list, loops): '''calculate the final library and object dependencies''' for t in tgt_list: # start with the maximum possible list - t.final_syslibs = t.direct_syslibs.union(t.indirect_syslibs) - t.final_libs = t.direct_libs.union(t.indirect_libs) - t.final_objects = t.direct_objects.union(t.indirect_objects) + t.final_syslibs = t.direct_syslibs.union(indirect_syslibs(bld, t, set(), loops)) + t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops)) + t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops)) for t in tgt_list: # don't depend on ourselves @@ -568,10 +587,6 @@ def calculate_final_deps(bld, tgt_list): if t.sname in t.final_objects: t.final_objects.remove(t.sname) - re_expand2(bld, tgt_list) - - loops = {} - # find any library loops for t in tgt_list: if t.samba_type in ['LIBRARY', 'PYTHON']: @@ -581,42 +596,43 @@ def calculate_final_deps(bld, tgt_list): # we could break this in either direction. If one of the libraries # has a version number, and will this be distributed publicly, then # we should make it the lower level library in the DAG - debug('deps: removing library loop %s<->%s', t.sname, l) + debug('deps: removing library loop %s from %s', t.sname, t2.sname) + dependency_loop(loops, t, t2.sname) t2.final_libs.remove(t.sname) - loops[t2.sname] = t.sname; - - re_expand2(bld, tgt_list) for type in ['BINARY']: - while True: - changed = False - for t in tgt_list: - if t.samba_type != type: continue - # if we will indirectly link to a target then we don't need it - new = t.final_objects.copy() - for l in t.final_libs: - t2 = bld.name_to_obj(l, bld.env) - dup = new.intersection(t2.expanded_targets2) - if dup: - debug('deps: removing dups from %s: %s also in %s %s', - t.sname, dup, t2.samba_type, l) - new = new.difference(dup) - changed = True - if changed: - t.final_objects = new - break - if not changed: - break + for t in tgt_list: + if t.samba_type != type: continue + # if we will indirectly link to a target then we don't need it + new = t.final_objects.copy() + for l in t.final_libs: + t2 = bld.name_to_obj(l, bld.env) + t2_obj = extended_objects(bld, t2, set()) + dup = new.intersection(t2_obj) + if dup: + debug('deps: removing dups from %s of type %s: %s also in %s %s', + t.sname, t.samba_type, dup, t2.samba_type, l) + new = new.difference(dup) + changed = True + t.final_objects = new + + for loop in loops: + debug('deps: Found dependency loops for target %s : %s', loop, loops[loop]) # we now need to make corrections for any library loops we broke up # any target that depended on the target of the loop and doesn't # depend on the source of the loop needs to get the loop source added - for type in ['BINARY','PYTHON']: + for type in ['BINARY','PYTHON','LIBRARY']: for t in tgt_list: if t.samba_type != type: continue for loop in loops: - if loop in t.final_libs and loops[loop] not in t.final_libs: - t.final_libs.add(loops[loop]) + if loop in t.final_libs: + diff = loops[loop].difference(t.final_libs) + if t.sname in diff: + diff.remove(t.sname) + if diff: + debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff) + t.final_libs = t.final_libs.union(diff) debug('deps: removed duplicate dependencies') @@ -743,6 +759,8 @@ def check_project_rules(bld): '''check the project rules - ensuring the targets are sane''' targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + loops = {} + inc_loops = {} # build a list of task generators we are interested in tgt_list = [] @@ -767,8 +785,8 @@ def check_project_rules(bld): expand_subsystem_deps(bld) build_direct_deps(bld, tgt_list) - build_indirect_deps(bld, tgt_list) - calculate_final_deps(bld, tgt_list) + break_dependency_loops(bld, tgt_list) + calculate_final_deps(bld, tgt_list, loops) # run the various attribute generators for f in [ build_dependencies, build_includes, add_init_functions ]: -- cgit