From: Andrew Tridgell Date: Sun, 18 Apr 2010 02:43:15 +0000 (+1000) Subject: build: added ABI checking to the WAF build X-Git-Tag: samba-3.6.0pre1~2830 X-Git-Url: http://git.samba.org/?p=samba.git;a=commitdiff_plain;h=d3dea9b1248edf9a3e96e88bea8de3e098fbc2fe build: added ABI checking to the WAF build See http://wiki.samba.org/index.php/Waf#ABI_Checking for details --- diff --git a/buildtools/wafsamba/samba_abi.py b/buildtools/wafsamba/samba_abi.py new file mode 100644 index 00000000000..6e4d8d81e08 --- /dev/null +++ b/buildtools/wafsamba/samba_abi.py @@ -0,0 +1,112 @@ +# functions for handling ABI checking of libraries + +import Options, Utils, os, Logs, samba_utils, sys, Task, fnmatch, re +from TaskGen import feature, before, after + +def normalise_signature(sig): + '''normalise a signature from gdb''' + sig = sig.strip() + sig = re.sub('^\$[0-9]+\s=\s\{*', '', sig) + sig = re.sub('\}(\s0x[0-9a-f]+\s<\w+>)?$', '', sig) + sig = re.sub('0x[0-9a-f]+', '0xXXXX', sig) + return sig + +def normalise_varargs(sig): + '''cope with older versions of gdb''' + sig = re.sub(',\s\.\.\.', '', sig) + return sig + +def parse_sigs(sigs, abi_match): + '''parse ABI signatures file''' + abi_match = samba_utils.TO_LIST(abi_match) + ret = {} + a = sigs.split('\n') + for s in a: + if s.find(':') == -1: + continue + sa = s.split(':') + if abi_match: + matched = False + for p in abi_match: + if fnmatch.fnmatch(sa[0], p): + matched = True + break + if not matched: + continue + ret[sa[0]] = normalise_signature(sa[1]) + return ret + +def save_sigs(sig_file, parsed_sigs): + '''save ABI signatures to a file''' + sigs = '' + for s in sorted(parsed_sigs.keys()): + sigs += '%s: %s\n' % (s, parsed_sigs[s]) + return samba_utils.save_file(sig_file, sigs, create_dir=True) + + +def abi_check_task(self): + '''check if the ABI has changed''' + abi_gen = self.ABI_GEN + + libpath = self.inputs[0].abspath(self.env) + libname = os.path.basename(libpath) + + sigs = Utils.cmd_output([abi_gen, libpath]) + parsed_sigs = parse_sigs(sigs, self.ABI_MATCH) + + sig_file = self.ABI_FILE + + old_sigs = samba_utils.load_file(sig_file) + if old_sigs is None or Options.options.ABI_UPDATE: + if not save_sigs(sig_file, parsed_sigs): + raise Utils.WafError('Failed to save ABI file "%s"' % sig_file) + Logs.warn('Generated ABI signatures %s' % sig_file) + return + + parsed_old_sigs = parse_sigs(old_sigs, self.ABI_MATCH) + + # check all old sigs + got_error = False + for s in parsed_old_sigs: + if not s in parsed_sigs: + Logs.error('%s: symbol %s has been removed - please update major version\n\tsignature: %s' % ( + libname, s, parsed_old_sigs[s])) + got_error = True + elif normalise_varargs(parsed_old_sigs[s]) != normalise_varargs(parsed_sigs[s]): + Logs.error('%s: symbol %s has changed - please update major version\n\told_signature: %s\n\tnew_signature: %s' % ( + libname, s, parsed_old_sigs[s], parsed_sigs[s])) + got_error = True + + for s in parsed_sigs: + if not s in parsed_old_sigs: + Logs.error('%s: symbol %s has been added - please mark it _PRIVATE_ or update minor version\n\tsignature: %s' % ( + libname, s, parsed_sigs[s])) + got_error = True + + if got_error: + raise Utils.WafError('ABI for %s has changed - please fix library version then build with --abi-update\nSee http://wiki.samba.org/index.php/Waf#ABI_Checking for more information' % libname) + + +t = Task.task_type_from_func('abi_check', abi_check_task, color='BLUE', ext_in='.bin') +t.quiet = True + +@after('apply_link') +@feature('abi_check') +def abi_check(self): + '''check that ABI matches saved signatures''' + env = self.bld.env + if not env.ABI_CHECK or self.abi_file is None: + return + + # if the platform doesn't support -fvisibility=hidden then the ABI + # checks become fairly meaningless + if not env.HAVE_VISIBILITY_ATTR: + return + + topsrc = self.bld.srcnode.abspath() + abi_gen = os.path.join(topsrc, 'buildtools/scripts/abi_gen.sh') + + tsk = self.create_task('abi_check', self.link_task.outputs[0]) + tsk.ABI_FILE = self.abi_file + tsk.ABI_MATCH = self.abi_match + tsk.ABI_GEN = abi_gen diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py index dca65959648..dd7abe2864c 100644 --- a/buildtools/wafsamba/samba_autoconf.py +++ b/buildtools/wafsamba/samba_autoconf.py @@ -604,7 +604,7 @@ def ADD_EXTRA_INCLUDES(conf, includes): -def CURRENT_CFLAGS(bld, target, cflags): +def CURRENT_CFLAGS(bld, target, cflags, hide_symbols=False): '''work out the current flags. local flags are added first''' if not 'EXTRA_CFLAGS' in bld.env: list = [] @@ -612,6 +612,8 @@ def CURRENT_CFLAGS(bld, target, cflags): list = bld.env['EXTRA_CFLAGS']; ret = TO_LIST(cflags) ret.extend(list) + if hide_symbols and bld.env.HAVE_VISIBILITY_ATTR: + ret.append('-fvisibility=hidden') return ret diff --git a/buildtools/wafsamba/samba_bundled.py b/buildtools/wafsamba/samba_bundled.py index 6a393ebf2c2..f668d56cb67 100644 --- a/buildtools/wafsamba/samba_bundled.py +++ b/buildtools/wafsamba/samba_bundled.py @@ -35,6 +35,7 @@ def BUILTIN_LIBRARY(bld, name): if bld.env.DISABLE_SHARED: return True return target_in_list(name, bld.env.BUILTIN_LIBRARIES, False) +Build.BuildContext.BUILTIN_LIBRARY = BUILTIN_LIBRARY def BUILTIN_DEFAULT(opt, builtins): diff --git a/buildtools/wafsamba/samba_utils.py b/buildtools/wafsamba/samba_utils.py index 94975c6519e..a3448b702a4 100644 --- a/buildtools/wafsamba/samba_utils.py +++ b/buildtools/wafsamba/samba_utils.py @@ -464,3 +464,39 @@ def CHECK_MAKEFLAGS(bld): Options.options.jobs = 1 Build.BuildContext.CHECK_MAKEFLAGS = CHECK_MAKEFLAGS + +option_groups = {} + +def option_group(opt, name): + '''find or create an option group''' + global option_groups + if name in option_groups: + return option_groups[name] + gr = opt.add_option_group(name) + option_groups[name] = gr + return gr +Options.Handler.option_group = option_group + + +def save_file(filename, contents, create_dir=False): + '''save data to a file''' + if create_dir: + mkdir_p(os.path.dirname(filename)) + try: + f = open(filename, 'w') + f.write(contents) + f.close() + except: + return False + return True + + +def load_file(filename): + '''return contents of a file''' + try: + f = open(filename, 'r') + r = f.read() + f.close() + except: + return None + return r diff --git a/buildtools/wafsamba/wafsamba.py b/buildtools/wafsamba/wafsamba.py index 633484bd682..1714718c36b 100644 --- a/buildtools/wafsamba/wafsamba.py +++ b/buildtools/wafsamba/wafsamba.py @@ -20,6 +20,7 @@ from samba_deps import * from samba_bundled import * import samba_install import samba_conftests +import samba_abi import tru64cc import irixcc import generic_cc @@ -108,6 +109,9 @@ def SAMBA_LIBRARY(bld, libname, source, target_type='LIBRARY', bundled_extension=True, link_name=None, + abi_file=None, + abi_match=None, + hide_symbols=False, enabled=True): '''define a Samba library''' @@ -142,6 +146,7 @@ def SAMBA_LIBRARY(bld, libname, source, autoproto = autoproto, depends_on = depends_on, needs_python = needs_python, + hide_symbols = hide_symbols, local_include = local_include) if libname == obj_target: @@ -165,6 +170,11 @@ def SAMBA_LIBRARY(bld, libname, source, features += ' pyext' elif needs_python: features += ' pyembed' + if abi_file: + features += ' abi_check' + + if abi_file: + abi_file = os.path.join(bld.curdir, abi_file) bld.SET_BUILD_GROUP(group) t = bld( @@ -181,7 +191,9 @@ def SAMBA_LIBRARY(bld, libname, source, samba_inst_path = install_path, name = libname, samba_realname = realname, - samba_install = install + samba_install = install, + abi_file = abi_file, + abi_match = abi_match ) if link_name: @@ -366,6 +378,7 @@ def SAMBA_SUBSYSTEM(bld, modname, source, use_hostcc=False, use_global_deps=True, vars=None, + hide_symbols=False, needs_python=False): '''define a Samba subsystem''' @@ -395,7 +408,7 @@ def SAMBA_SUBSYSTEM(bld, modname, source, features = features, source = source, target = modname, - samba_cflags = CURRENT_CFLAGS(bld, modname, cflags), + samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols), depends_on = depends_on, samba_deps = TO_LIST(deps), samba_includes = includes, diff --git a/buildtools/wafsamba/wscript b/buildtools/wafsamba/wscript index b4fcb996848..a611797b473 100644 --- a/buildtools/wafsamba/wscript +++ b/buildtools/wafsamba/wscript @@ -12,70 +12,88 @@ def set_options(opt): opt.tool_options('gnu_dirs') - opt.add_option('--bundled-libraries', + gr = opt.add_option_group('library handling options') + + gr.add_option('--bundled-libraries', help=("comma separated list of bundled libraries. May include !LIBNAME to disable bundling a library. Can be 'NONE' or 'ALL' [auto]"), action="store", dest='BUNDLED_LIBS', default='') extension_default = Options.options['BUNDLED_EXTENSION_DEFAULT'] - opt.add_option('--bundled-library-extension', + gr.add_option('--bundled-library-extension', help=("name extension for bundled libraries [%s]" % extension_default), action="store", dest='BUNDLED_EXTENSION', default=extension_default) extension_exception = Options.options['BUNDLED_EXTENSION_EXCEPTION'] - opt.add_option('--bundled-extension-exception', + gr.add_option('--bundled-extension-exception', help=("comman separated list of libraries to not apply extension to [%s]" % extension_exception), action="store", dest='BUNDLED_EXTENSION_EXCEPTION', default=extension_exception) builtin_defauilt = Options.options['BUILTIN_LIBRARIES_DEFAULT'] - opt.add_option('--builtin-libraries', + gr.add_option('--builtin-libraries', help=("command separated list of libraries to build directly into binaries [%s]" % builtin_defauilt), action="store", dest='BUILTIN_LIBRARIES', default=builtin_defauilt) - opt.add_option('--minimum-library-version', + gr.add_option('--minimum-library-version', help=("list of minimum system library versions (LIBNAME1:version,LIBNAME2:version)"), action="store", dest='MINIMUM_LIBRARY_VERSION', default='') - opt.add_option('--with-modulesdir', - help=("modules directory [PREFIX/modules]"), - action="store", dest='MODULESDIR', default='${PREFIX}/modules') - opt.add_option('--disable-shared', + gr.add_option('--disable-shared', help=("Disable all use of shared libraries"), action="store_true", dest='disable_shared', default=False) - opt.add_option('--disable-rpath', + gr.add_option('--disable-rpath', help=("Disable use of rpath for build binaries"), action="store_true", dest='disable_rpath_build', default=False) - opt.add_option('--disable-rpath-install', + gr.add_option('--disable-rpath-install', help=("Disable use of rpath for installed binaries"), action="store_true", dest='disable_rpath_install', default=False) - opt.add_option('--enable-developer', + + opt.add_option('--with-modulesdir', + help=("modules directory [PREFIX/modules]"), + action="store", dest='MODULESDIR', default='${PREFIX}/modules') + + gr = opt.option_group('developer options') + + gr.add_option('-C', + help='enable configure cacheing', + action='store_true', dest='enable_configure_cache') + gr.add_option('--enable-developer', help=("Turn on developer warnings and debugging"), action="store_true", dest='developer', default=False) - opt.add_option('--picky-developer', + gr.add_option('--picky-developer', help=("Treat all warnings as errors (enable -Werror)"), action="store_true", dest='picky_developer', default=False) - opt.add_option('--fatal-errors', + gr.add_option('--fatal-errors', help=("Stop compilation on first error (enable -Wfatal-errors)"), action="store_true", dest='fatal_errors', default=False) - opt.add_option('--enable-gccdeps', - help=("Enable use gcc -MD dependency module"), + gr.add_option('--enable-gccdeps', + help=("Enable use of gcc -MD dependency module"), action="store_true", dest='enable_gccdeps', default=False) - opt.add_option('--timestamp-dependencies', + gr.add_option('--timestamp-dependencies', help=("use file timestamps instead of content for build dependencies (BROKEN)"), action="store_true", dest='timestamp_dependencies', default=False) - opt.add_option('-C', - help='enable configure cacheing', - action='store_true', dest='enable_configure_cache') - opt.add_option('--pedantic', + gr.add_option('--pedantic', help=("Enable even more compiler warnings"), action='store_true', dest='pedantic', default=False) - opt.add_option('--cross-compile', + gr.add_option('--abi-check', + help=("Check ABI signatures for libraries"), + action='store_true', dest='ABI_CHECK', default=False) + gr.add_option('--abi-check-disable', + help=("Disable ABI checking (used with --enable-developer)"), + action='store_true', dest='ABI_CHECK_DISABLE', default=False) + gr.add_option('--abi-update', + help=("Update ABI signature files for libraries"), + action='store_true', dest='ABI_UPDATE', default=False) + + gr = opt.add_option_group('cross compilation options') + + gr.add_option('--cross-compile', help=("configure for cross-compilation"), action='store_true', dest='CROSS_COMPILE', default=False) - opt.add_option('--cross-execute', + gr.add_option('--cross-execute', help=("command prefix to use for cross-execution in configure"), action='store', dest='CROSS_EXECUTE', default='') - opt.add_option('--hostcc', + gr.add_option('--hostcc', help=("set host compiler when cross compiling"), action='store', dest='HOSTCC', default=False) @@ -147,6 +165,11 @@ def configure(conf): Logs.error('ERROR: --program-prefix not supported') sys.exit(1) + # enable ABI checking for developers + conf.env.ABI_CHECK = Options.options.ABI_CHECK or Options.options.developer + if Options.options.ABI_CHECK_DISABLE: + conf.env.ABI_CHECK = False + # see if we can compile and run a simple C program conf.CHECK_CODE('printf("hello world\\n")', define='HAVE_SIMPLE_C_PROG',