# Samba automatic dependency handling and project rules
-import Build, os, re, Environment
+import Build, os, re, Environment, Logs
from samba_utils import *
from samba_autoconf import *
+from samba_bundled import BUILTIN_LIBRARY
@conf
def ADD_GLOBAL_DEPENDENCY(ctx, dep):
'''define an alias for a target name'''
cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
if alias in cache:
- print("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
- raise
+ Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
+ sys.exit(1)
cache[alias] = target
Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
+@conf
+def SET_SYSLIB_DEPS(conf, target, deps):
+ '''setup some implied dependencies for a SYSLIB'''
+ cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
+ cache[target] = deps
+
+
def EXPAND_ALIAS(bld, target):
'''expand a target name via an alias'''
aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
the full dependency list for a target until we have all of the targets declared.
'''
- # we only should add extra library and object deps on libraries and binaries
- if not self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
- return
-
- # we need to link against:
+ if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
+ self.uselib = list(self.final_syslibs)
+ self.uselib_local = list(self.final_libs)
+ self.add_objects = list(self.final_objects)
- # 1) any direct system libs
- # 2) any indirect system libs that come from subsystem dependencies
- # 3) any direct local libs
- # 4) any indirect local libs that come from subsystem dependencies
- # 5) any direct objects
- # 6) any indirect objects that come from subsystem dependencies
+ # extra link flags from pkg_config
+ libs = self.final_syslibs.copy()
- self.uselib = list(self.final_syslibs)
- self.uselib_local = list(self.final_libs)
- self.add_objects = list(self.final_objects)
+ (ccflags, ldflags) = library_flags(self, list(libs))
+ new_ldflags = getattr(self, 'ldflags', [])
+ new_ldflags.extend(ldflags)
+ self.ldflags = new_ldflags
- debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
- self.sname, self.uselib, self.uselib_local, self.add_objects)
+ debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
+ self.sname, self.uselib, self.uselib_local, self.add_objects)
+ if self.samba_type in ['SUBSYSTEM']:
+ # this is needed for the ccflags of libs that come from pkg_config
+ self.uselib = list(self.direct_syslibs)
+ if getattr(self, 'uselib', None):
+ up_list = []
+ for l in self.uselib:
+ up_list.append(l.upper())
+ self.uselib = up_list
def build_includes(self):
'''This builds the right set of includes for a target.
seen = set()
- '''
- # this was useful for finding problems with the autogenerated rules
- for t in tgt_list:
- base_list = set()
- sources = TO_LIST(getattr(t, 'source', ''))
- for s in sources:
- bname = os.path.basename(s)
- if bname in base_list:
- print "Suspicious duplicate name %s in %s" % (bname, t.sname)
- continue
- base_list.add(bname)
- '''
-
-
for t in tgt_list:
obj_sources = getattr(t, 'source', '')
- tpath = os_path_relpath(t.path.abspath(bld.env), t.env['BUILD_DIRECTORY'] + '/default')
+ tpath = os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default')
obj_sources = bld.SUBDIR(tpath, obj_sources)
t.samba_source_set = set(TO_LIST(obj_sources))
if s['dep'] == s2['dep']: continue
common = s['src'].intersection(s2['src'])
if common.difference(seen):
- print("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
+ Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
s['dep'], s2['dep'],
common))
seen = seen.union(common)
type = target_dict[t.sname]
if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
if re.search('^PIDL_', t.sname) is None:
- print "Target %s of type %s is unused by any other target" % (t.sname, type)
+ Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
def show_final_deps(bld, tgt_list):
'''build the direct_objects and direct_libs sets for each target'''
targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
+ syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
global_deps = bld.env.GLOBAL_DEPENDENCIES
for t in tgt_list:
t.direct_libs = set()
t.direct_syslibs = set()
deps = t.samba_deps_extended
- deps.extend(global_deps)
+ if getattr(t, 'samba_use_global_deps', False):
+ 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
+ Logs.error("Unknown dependency %s in %s" % (d, t.sname))
+ sys.exit(1)
if targets[d] in [ 'EMPTY', 'DISABLED' ]:
continue
if targets[d] == 'SYSLIB':
t.direct_syslibs.add(d)
+ if d in syslib_deps:
+ for implied in TO_LIST(syslib_deps[d]):
+ if BUILTIN_LIBRARY(bld, implied):
+ t.direct_objects.add(implied)
+ else:
+ t.direct_libs.add(implied)
continue
t2 = bld.name_to_obj(d, bld.env)
if t2 is None:
- print "no task %s type %s" % (d, targets[d])
+ Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
+ sys.exit(1)
if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
t.direct_libs.add(d)
elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
return ret
-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
- '''
-
- 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, loops)
- chain.remove(obj)
- ret = ret.union(t2.direct_syslibs)
- ret = ret.union(r2)
-
- t.indirect_syslibs = ret
- return ret
-
-
def indirect_objects(bld, t, chain, loops):
'''recursively calculate the indirect object dependencies for a target
if ret is not None: return ret
ret = set()
- ret = ret.union(t.direct_objects)
- ret = ret.union(t.indirect_objects)
+ ret = ret.union(t.final_objects)
- for lib in t.direct_libs:
+ for lib in t.final_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(t2.final_objects)
ret = ret.union(r2)
t.extended_objects = ret
for loop in loops:
debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
+ for loop in inc_loops:
+ debug('deps: Found include loops for target %s : %s', loop, inc_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])
+ for loop in inc_loops.copy():
+ for tgt in inc_loops[loop]:
+ if tgt in inc_loops:
+ inc_loops[loop] = inc_loops[loop].union(inc_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)
+ loops[loop] = loops[loop].union(t.indirect_objects)
+ loops[loop] = loops[loop].union(t.direct_objects)
+ if t.samba_type in ['LIBRARY','PYTHON']:
+ loops[loop] = loops[loop].union(t.indirect_libs)
+ loops[loop] = loops[loop].union(t.direct_libs)
if loop in loops[loop]:
loops[loop].remove(loop)
+ # expand indirect includes loops
+ for loop in inc_loops.copy():
+ t = bld.name_to_obj(loop, bld.env)
+ inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
+ if loop in inc_loops[loop]:
+ inc_loops[loop].remove(loop)
+
# add in the replacement dependencies
for t in tgt_list:
for loop in loops:
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)
- # now calculate the indirect syslibs, which can change from the loop expansion
+ for loop in inc_loops:
+ objs = getattr(t, 'includes_objects', set())
+ if loop in objs:
+ diff = inc_loops[loop].difference(objs)
+ if t.sname in diff:
+ diff.remove(t.sname)
+ if diff:
+ debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
+ objs = objs.union(diff)
+ setattr(t, 'includes_objects', objs)
+
+
+def reduce_objects(bld, tgt_list):
+ '''reduce objects by looking for indirect object dependencies'''
+ rely_on = {}
+
for t in tgt_list:
- indirect_syslibs(bld, t, set(), loops)
+ t.extended_objects = None
+
+ changed = False
+
+ for type in ['BINARY', 'PYTHON', 'LIBRARY']:
+ 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
+ if not l in rely_on:
+ rely_on[l] = set()
+ rely_on[l] = rely_on[l].union(dup)
+ t.final_objects = new
+
+ if not changed:
+ return False
+
+ # add back in any objects that were relied upon by the reduction rules
+ for r in rely_on:
+ t = bld.name_to_obj(r, bld.env)
+ t.final_objects = t.final_objects.union(rely_on[r])
+
+ return True
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(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))
if t.sname in t.final_objects:
t.final_objects.remove(t.sname)
+
# find any library loops
for t in tgt_list:
if t.samba_type in ['LIBRARY', 'PYTHON']:
dependency_loop(loops, t, t2.sname)
t2.final_libs.remove(t.sname)
- for type in ['BINARY']:
- 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','LIBRARY']:
+ for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
for t in tgt_list:
if t.samba_type != type: continue
for loop in loops:
debug('deps: Expanded target %s by loop %s libraries %s', t.sname, loop, diff)
t.final_libs = t.final_libs.union(diff)
+ # remove objects that are also available in linked libs
+ count = 0
+ while reduce_objects(bld, tgt_list):
+ count += 1
+ if count > 100:
+ Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
+ break
+ debug('deps: Object reduction took %u iterations', count)
+
+ # add in any syslib dependencies
+ for t in tgt_list:
+ if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
+ continue
+ syslibs = set()
+ for d in t.final_objects:
+ t2 = bld.name_to_obj(d, bld.env)
+ syslibs = syslibs.union(t2.direct_syslibs)
+ # this adds the indirect syslibs as well, which may not be needed
+ # depending on the linker flags
+ for d in t.final_libs:
+ t2 = bld.name_to_obj(d, bld.env)
+ syslibs = syslibs.union(t2.direct_syslibs)
+ t.final_syslibs = syslibs
+
debug('deps: removed duplicate dependencies')
savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
savedeps_outenv = ['INC_PATHS']
-savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
+savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
savedeps_files = ['buildtools/wafsamba/samba_deps.py']
def save_samba_deps(bld, tgt_list):
denv.store(depsfile)
+
def load_samba_deps(bld, tgt_list):
'''load a previous set of build dependencies if possible'''
depsfile = os.path.join(bld.bdir, "sambadeps")
return True
+
def check_project_rules(bld):
'''check the project rules - ensuring the targets are sane'''
continue
t = bld.name_to_obj(tgt, bld.env)
if t is None:
- print "Target %s of type %s has no task generator" % (tgt, type)
- raise
+ Logs.error("Target %s of type %s has no task generator" % (tgt, type))
+ sys.exit(1)
tgt_list.append(t)
add_samba_attributes(bld, tgt_list)
if load_samba_deps(bld, tgt_list):
return
- print "Checking project rules ..."
+ Logs.info("Checking project rules ...")
debug('deps: project rules checking started')
#check_orpaned_targets(bld, tgt_list)
if not check_duplicate_sources(bld, tgt_list):
- print "Duplicate sources present - aborting"
+ Logs.error("Duplicate sources present - aborting")
sys.exit(1)
show_final_deps(bld, tgt_list)
save_samba_deps(bld, tgt_list)
- print "Project rules pass"
+ Logs.info("Project rules pass")
def CHECK_PROJECT_RULES(bld):