From fffa6a5b488058d984d61b0cd1fe1fb550a196b4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 17 Mar 2010 22:07:11 +1100 Subject: build: separate out dependencies and python rules --- buildtools/wafsamba/samba_deps.py | 390 ++++++++++++++++++++++++++++++++++++ buildtools/wafsamba/samba_python.py | 44 ++++ 2 files changed, 434 insertions(+) create mode 100644 buildtools/wafsamba/samba_deps.py create mode 100644 buildtools/wafsamba/samba_python.py diff --git a/buildtools/wafsamba/samba_deps.py b/buildtools/wafsamba/samba_deps.py new file mode 100644 index 0000000000..41b786d6bf --- /dev/null +++ b/buildtools/wafsamba/samba_deps.py @@ -0,0 +1,390 @@ +# Samba automatic dependency handling + +from TaskGen import taskgen, before +import Build, os, string, Utils, re +from samba_utils import * +from samba_autoconf import * + +@conf +def ADD_GLOBAL_DEPENDENCY(ctx, dep): + '''add a dependency for all binaries and libraries''' + if not 'GLOBAL_DEPENDENCIES' in ctx.env: + ctx.env.GLOBAL_DEPENDENCIES = [] + ctx.env.GLOBAL_DEPENDENCIES.append(dep) + + +def TARGET_ALIAS(bld, target, alias): + '''define an alias for a target name''' + cache = LOCAL_CACHE(bld, 'TARGET_ALIAS') + bld.ASSERT(alias not in cache, "Target alias %s already set" % alias) + cache[alias] = target +Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS + + +def EXPAND_ALIAS(bld, target): + '''expand a target name via an alias''' + aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS') + if target in aliases: + return aliases[target] + return target +Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS + + +def expand_dependencies(bld, dep, chain, path): + '''expand a dependency recursively + return a triple of (uselib, uselib_local, add_objects) + ''' + + dep = EXPAND_ALIAS(bld, dep) + + t = bld.name_to_obj(dep, bld.env) + + # check for a cached list + if t is not None: + expanded = getattr(t, 'expanded_dependencies', None) + if expanded is not None: + return expanded + + target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE') + + uselib_local = [] + uselib = [] + add_objects = [] + + recurse = False + + bld.ASSERT(dep in target_dict, "Dependency %s not found in %s" % (dep, path)) + type = target_dict[dep] + if type == 'SYSLIB': + uselib.append(dep) + elif type == 'LIBRARY': + uselib_local.append(dep) + recurse = True + elif type == 'SUBSYSTEM': + add_objects.append(dep) + recurse = True + elif type == 'MODULE': + add_objects.append(dep) + recurse = True + elif type == 'PYTHON': + add_objects.append(dep) + recurse = True + elif type == 'ASN1': + add_objects.append(dep) + recurse = True + elif type == 'BINARY': + pass + elif type == 'EMPTY': + pass + elif type == 'DISABLED': + debug('deps: Ignoring dependency on disabled target %s: %s' % (dep, path)) + else: + bld.ASSERT(False, "Unknown target type %s for %s" % (type, dep)) + + # for some types we have to build the list recursively + if recurse: + bld.ASSERT(t is not None, "Unable to find target %s" % dep) + rec_deps = getattr(t, 'samba_deps', None) + bld.ASSERT(rec_deps is not None, "Unable to find dependencies of target %s" % dep) + for d2 in rec_deps: + try: + bld.ASSERT(d2 not in chain, "Circular dependency for %s: %s->%s" % (dep, path, d2)) + except: + print "Removing dependency %s from target %s" % (d2, dep) + rec_deps.remove(d2) + continue + c2 = chain.copy() + c2[d2] = True + (rec_uselib, rec_uselib_local, + rec_add_objects) = expand_dependencies(bld, d2, c2, "%s->%s" % (path, d2)) + uselib.extend(rec_uselib) + uselib_local.extend(rec_uselib_local) + add_objects.extend(rec_add_objects) + + if t is not None: + t.expanded_dependencies = (uselib, uselib_local, add_objects) + + return (uselib, uselib_local, add_objects) + + +def expand_deplist(self): + '''return an expanded list of dependencies from the samba_deps attribute''' + + if not getattr(self, 'samba_deps', None): + return ([], [], []) + + bld = self.bld + deps = self.samba_deps + + uselib_local = [] + uselib = [] + add_objects = [] + + for d in deps: + (u, ul, ao) = expand_dependencies(bld, d, { self.name:True }, self.name) + uselib.extend(u) + uselib_local.extend(ul) + add_objects.extend(ao) + + return (uselib, uselib_local, add_objects) + + + +@feature('cc', 'cshlib', 'cprogram') +@before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths', 'build_includes') +@after('default_cc') +def build_dependencies(self): + '''This builds the dependency list for a target. It runs after all the targets are declared + + The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing + the full dependency list for a target until we have all of the targets declared. So what we do is + add a samba_deps attribute on the task generator when we declare it, then + this rule runs after all the task generators are declared and maps the samba_deps attribute + to a set of uselib, uselib_local and add_objects dependencies + ''' + + if getattr(self, 'build_dependencies_done', False): + return + self.build_dependencies_done = True + + if getattr(self, 'samba_deps', None) is None: + return + + target_dict = LOCAL_CACHE(self.bld, 'TARGET_TYPE') + + # we only should add extra library and object deps on libraries and binaries + type = target_dict[self.name] + if type != 'LIBRARY' and type != 'BINARY': + return + + (uselib, uselib_local, add_objects) = expand_deplist(self) + + if 'GLOBAL_DEPENDENCIES' in self.bld.env: + add_objects.extend(self.bld.env.GLOBAL_DEPENDENCIES) + + self.uselib = unique_list(uselib) + self.uselib_local = unique_list(uselib_local) + self.add_objects = unique_list(add_objects) + + debug('deps: dependency counts for %s: uselib=%u uselib_local=%u add_objects=%u' % ( + self.name, len(uselib), len(uselib_local), len(add_objects))) + + + +@feature('cc', 'cshlib', 'cprogram') +@before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths', 'add_init_functions') +@after('build_dependencies') +def build_includes(self): + '''This builds the right set of includes for a target. + + This is closely related to building the set of dependencies, and + calls into the same expand_dependencies() function to do the work. + + One tricky part of this is that the includes= attribute for a + target needs to use paths which are relative to that targets + declaration directory (which we can get at via t.path). + + The way this works is the includes list gets added as + samba_includes in the main build task declaration. Then this + function runs after all of the tasks are declared, and it + processes the samba_includes attribute to produce a includes= + attribute + ''' + + if not getattr(self, 'build_dependencies_done', False): + build_dependencies(self) + if getattr(self, 'build_includes_done', False): + return + self.build_includes_done = True + + if getattr(self, 'samba_includes', None) is None: + return + + bld = self.bld + + (uselib, uselib_local, add_objects) = expand_deplist(self) + + # get the list of all dependencies + all_deps = [] +# all_deps.extend(uselib) + all_deps.extend(uselib_local) + all_deps.extend(add_objects) + all_deps = unique_list(all_deps) + + includes = [] + + # build a list of includes + if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True): + includes.append('.') + + includes.extend(TO_LIST(self.samba_includes)) + + if 'EXTRA_INCLUDES' in bld.env: + includes.extend(bld.env['EXTRA_INCLUDES']) + + includes.append('#') + + mypath = self.path.abspath(bld.env) + + for d in all_deps: + t = bld.name_to_obj(d, bld.env) + bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.name)) + t.samba_used = True + samba_includes = getattr(t, 'samba_includes', None) + inclist = TO_LIST(samba_includes) + if getattr(t, 'local_include', True) == True: + inclist.append('.') + if inclist == []: + continue + tpath = t.path.abspath(bld.env) + relpath = os.path.relpath(tpath, mypath) + for inc in inclist: + includes.append(os.path.normpath(os.path.join(relpath, inc))) + + if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True): + includes.append('.') + + self.includes = unique_list(includes) + debug('deps: Target %s has includes=%s all_deps=%s' % (self.name, self.includes, all_deps)) + + +@feature('cc', 'cshlib', 'cprogram') +@before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths') +@after('build_includes') +def add_init_functions(self): + '''This builds the right set of init functions''' + + if not getattr(self, 'build_includes_done', False): + build_includes(self) + if getattr(self, 'add_init_functions_done', False): + return + self.add_init_functions_done = True + + bld = self.bld + + subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS') + + modules = [] + if self.name in subsystems: + modules.append(self.name) + + m = getattr(self, 'samba_modules', None) + if m is not None: + modules.extend(TO_LIST(m)) + + m = getattr(self, 'samba_subsystem', None) + if m is not None: + modules.append(m) + + if modules == []: + return + + cflags = getattr(self, 'ccflags', []) + for m in modules: + if not m in subsystems: + print "subsystems: %s" % subsystems + bld.ASSERT(m in subsystems, + "No init_function defined for module '%s' in target '%s'" % (m, self.name)) + cflags.append('-DSTATIC_%s_MODULES="%s"' % (m, ','.join(subsystems[m]))) + self.ccflags = cflags + + +def check_orpaned_targets(bld): + '''check if any build targets are orphaned''' + + target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE') + + # make sure all the earlier functions have run + for t in bld.all_task_gen: + if not t.name in target_dict: + continue + if not getattr(t, 'add_init_functions_done', False): + add_init_functions(t) + + for t in bld.all_task_gen: + if not t.name in target_dict: + continue + if getattr(t, 'samba_used', False) == True: + continue + type = target_dict[t.name] + if type != 'BINARY' and type != 'LIBRARY' and type != 'MODULE': + if re.search('^PIDL_', t.name) is None: + print "Target %s of type %s is unused by any other target" % (t.name, type) + + +def CHECK_ORPANED_TARGETS(bld): + bld.add_pre_fun(check_orpaned_targets) +Build.BuildContext.CHECK_ORPANED_TARGETS = CHECK_ORPANED_TARGETS + + +@feature('dfkj*') +def samba_post_process(self): + '''samba specific post processing of task''' + if getattr(self, 'meths', None) is None: + return + count = getattr(self, 'moved_to_end', 0) + if count < 10: + # there has got to be a better way!! + self.moved_to_end = count + 1 + self.meths.append('samba_post_process') + return + + samba_post = getattr(self, 'samba_post', None) + if samba_post is None: + return + (tgt, cmd) = samba_post + self.env.TARGET_DIRECTORY = self.path.abspath(self.env) + #print "cmd=%s tgt=%s" % (cmd, tgt) + cmd = Utils.subst_vars(cmd, self.env) + tgt = Utils.subst_vars(tgt, self.env) + if os.path.isfile(tgt): + debug('deps: post processing for %s: %s' % (self.name, cmd)) + ret = os.system(cmd) + self.bld.ASSERT(ret == 0, "Post processing for %s failed (%d): %s" % (self.name, ret, cmd)) + + +############################## +# handle the creation of links for libraries and binaries +# note that we use a relative symlink path to allow the whole tree +# to me moved/copied elsewhere without breaking the links +t = Task.simple_task_type('symlink_lib', 'ln -sf ../${SRC} ${LINK_TARGET}', color='PINK', + ext_in='.bin') +t.quiet = True + +@feature('symlink_lib') +@after('apply_link') +def symlink_lib(self): + tsk = self.create_task('symlink_lib', self.link_task.outputs[0]) + + # calculat the link target and put it in the environment + soext="" + vnum = getattr(self, 'vnum', None) + if vnum is not None: + soext = '.' + vnum.split('.')[0] + + libname = self.target + tsk.env.LINK_TARGET = '%s/lib%s.so%s' % (LIB_PATH, libname, soext) + debug('task_gen: LINK_TARGET for %s is %s', self.name, tsk.env.LINK_TARGET) + + +# for binaries we need to copy the executable to avoid the rpath changing +# in the local bin/ directory on install +t = Task.simple_task_type('copy_bin', 'rm -f ${BIN_TARGET} && cp ${SRC} ${BIN_TARGET}', color='PINK', + ext_in='.bin', shell=True) +t.quiet = True + +@feature('copy_bin') +@after('apply_link') +def copy_bin(self): + if Options.is_install: + # we don't want to copy the install binary, as + # that has the install rpath, not the build rpath + # The rpath of the binaries in bin/default/foo/blah is different + # during the install phase, as distros insist on not using rpath in installed binaries + return + tsk = self.create_task('copy_bin', self.link_task.outputs[0]) + + tsk.env.BIN_TARGET = self.target + debug('task_gen: BIN_TARGET for %s is %s', self.name, tsk.env.BIN_TARGET) + + diff --git a/buildtools/wafsamba/samba_python.py b/buildtools/wafsamba/samba_python.py new file mode 100644 index 0000000000..0845a7e61c --- /dev/null +++ b/buildtools/wafsamba/samba_python.py @@ -0,0 +1,44 @@ +# waf build tool for building IDL files with pidl + +from TaskGen import taskgen, before +import Build, os, string, Utils +from samba_utils import * +from samba_autoconf import * + + +def SAMBA_PYTHON(bld, name, + source='', + deps='', + public_deps='', + realname=None, + cflags='', + includes='', + init_function_sentinal=None, + local_include=True, + enabled=True): + '''build a python extension for Samba''' + + if not enabled: + SET_TARGET_TYPE(bld, name, 'DISABLED') + return + + if not SET_TARGET_TYPE(bld, name, 'PYTHON'): + return + + deps += ' ' + public_deps + + # when we support static python modules we'll need to gather + # the list from all the SAMBA_PYTHON() targets + if init_function_sentinal is not None: + cflags += '-DSTATIC_LIBPYTHON_MODULES="%s"' % init_function_sentinal + + t = bld( + features = 'cc cshlib pyext', + source = source, + target = name, + ccflags = CURRENT_CFLAGS(bld, name, cflags), + samba_includes = includes, + local_include = local_include, + samba_deps = TO_LIST(deps) + ) +Build.BuildContext.SAMBA_PYTHON = SAMBA_PYTHON -- cgit