wafsamba: Fix 'make -j<jobs>'
[samba.git] / buildtools / wafsamba / samba_abi.py
index bd074f8f275cc3fb96d31b3808e1abf9f7b8af47..8ddf209045f98d2f99a628326dd3707a7163f1c4 100644 (file)
@@ -1,7 +1,13 @@
 # functions for handling ABI checking of libraries
 
-import Options, Utils, os, Logs, samba_utils, sys, Task, fnmatch, re, Build
-from TaskGen import feature, before, after
+import os
+import sys
+import re
+import fnmatch
+
+from waflib import Options, Utils, Logs, Task, Build, Errors
+from waflib.TaskGen import feature, before, after
+import samba_utils
 
 # these type maps cope with platform specific names for common types
 # please add new type mappings into the list below
@@ -10,12 +16,16 @@ abi_type_maps = {
     'struct __va_list_tag *' : 'va_list'
     }
 
+version_key = lambda x: map(int, x.split("."))
+
 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('^\$[0-9]+\s=\s\{(.+)\}$', r'\1', sig)
+    sig = re.sub('^\$[0-9]+\s=\s\{(.+)\}(\s0x[0-9a-f]+\s<\w+>)+$', r'\1', sig)
+    sig = re.sub('^\$[0-9]+\s=\s(0x[0-9a-f]+)\s?(<\w+>)?$', r'\1', sig)
     sig = re.sub('0x[0-9a-f]+', '0xXXXX', sig)
+    sig = re.sub('", <incomplete sequence (\\\\[a-z0-9]+)>', r'\1"', sig)
 
     for t in abi_type_maps:
         # we need to cope with non-word characters in mapped types
@@ -28,11 +38,13 @@ def normalise_signature(sig):
         sig = re.sub(m, abi_type_maps[t], 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)
@@ -44,14 +56,17 @@ def parse_sigs(sigs, abi_match):
         sa = s.split(':')
         if abi_match:
             matched = False
+            negative = False
             for p in abi_match:
                 if p[0] == '!' and fnmatch.fnmatch(sa[0], p[1:]):
+                    negative = True
                     break
                 elif fnmatch.fnmatch(sa[0], p):
                     matched = True
                     break
-            if not matched:
+            if (not matched) and negative:
                 continue
+        Logs.debug("%s -> %s" % (sa[1], normalise_signature(sa[1])))
         ret[sa[0]] = normalise_signature(sa[1])
     return ret
 
@@ -78,7 +93,7 @@ def abi_check_task(self):
     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)
+            raise Errors.WafError('Failed to save ABI file "%s"' % sig_file)
         Logs.warn('Generated ABI signatures %s' % sig_file)
         return
 
@@ -103,14 +118,14 @@ def abi_check_task(self):
             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)
+        raise Errors.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\nIf you have not changed any ABI, and your platform always gives this error, please configure with --abi-check-disable to skip this check' % libname)
 
 
-t = Task.task_type_from_func('abi_check', abi_check_task, color='BLUE', ext_in='.bin')
+t = Task.task_factory('abi_check', abi_check_task, color='BLUE', ext_in='.bin')
 t.quiet = True
 # allow "waf --abi-check" to force re-checking the ABI
 if '--abi-check' in sys.argv:
-    Task.always_run(t)
+    t.always_run = True
 
 @after('apply_link')
 @feature('abi_check')
@@ -128,7 +143,8 @@ def abi_check(self):
     topsrc = self.bld.srcnode.abspath()
     abi_gen = os.path.join(topsrc, 'buildtools/scripts/abi_gen.sh')
 
-    abi_file = "%s/%s-%s.sigs" % (self.abi_directory, self.name, self.vnum)
+    abi_file = "%s/%s-%s.sigs" % (self.abi_directory, self.version_libname,
+                                  self.vnum)
 
     tsk = self.create_task('abi_check', self.link_task.outputs[0])
     tsk.ABI_FILE = abi_file
@@ -138,39 +154,59 @@ def abi_check(self):
 
 def abi_process_file(fname, version, symmap):
     '''process one ABI file, adding new symbols to the symmap'''
-    f = open(fname, mode='r')
-    for line in f:
+    for line in Utils.readf(fname).splitlines():
         symname = line.split(":")[0]
         if not symname in symmap:
             symmap[symname] = version
-    f.close()
 
-def abi_write_vscript(vscript, libname, version, symmap, abi_match):
-    '''write a vscript file for a library in --version-script format'''
+
+def abi_write_vscript(f, libname, current_version, versions, symmap, abi_match):
+    """Write a vscript file for a library in --version-script format.
+
+    :param f: File-like object to write to
+    :param libname: Name of the library, uppercased
+    :param current_version: Current version
+    :param versions: Versions to consider
+    :param symmap: Dictionary mapping symbols -> version
+    :param abi_match: List of symbols considered to be public in the current
+        version
+    """
 
     invmap = {}
     for s in symmap:
         invmap.setdefault(symmap[s], []).append(s)
 
-    f = open(vscript, mode='w')
     last_key = ""
-    for k in sorted(invmap):
+    versions = sorted(versions, key=version_key)
+    for k in versions:
         symver = "%s_%s" % (libname, k)
-        if symver == version:
+        if symver == current_version:
             break
-        f.write("%s {\n\tglobal: \n" % symver)
-        for s in invmap[k]:
-            f.write("\t\t%s;\n" % s);
+        f.write("%s {\n" % symver)
+        if k in sorted(invmap.keys()):
+            f.write("\tglobal:\n")
+            for s in invmap.get(k, []):
+                f.write("\t\t%s;\n" % s);
         f.write("}%s;\n\n" % last_key)
         last_key = " %s" % symver
-    f.write("%s {\n" % version)
+    f.write("%s {\n" % current_version)
+    local_abi = filter(lambda x: x[0] == '!', abi_match)
+    global_abi = filter(lambda x: x[0] != '!', abi_match)
     f.write("\tglobal:\n")
-    for x in abi_match:
-        f.write("\t\t%s;\n" % x)
-    if abi_match != ["*"]:
-        f.write("\tlocal: *;\n")
+    if len(global_abi) > 0:
+        for x in global_abi:
+            f.write("\t\t%s;\n" % x)
+    else:
+        f.write("\t\t*;\n")
+    # Always hide symbols that must be local if exist
+    local_abi.extend(["!_end", "!__bss_start", "!_edata"])
+    f.write("\tlocal:\n")
+    for x in local_abi:
+        f.write("\t\t%s;\n" % x[1:])
+    if global_abi != ["*"]:
+        if len(global_abi) > 0:
+            f.write("\t\t*;\n")
     f.write("};\n")
-    f.close()
 
 
 def abi_build_vscript(task):
@@ -179,24 +215,33 @@ def abi_build_vscript(task):
     tgt = task.outputs[0].bldpath(task.env)
 
     symmap = {}
-
+    versions = []
     for f in task.inputs:
         fname = f.abspath(task.env)
         basename = os.path.basename(fname)
         version = basename[len(task.env.LIBNAME)+1:-len(".sigs")]
+        versions.append(version)
         abi_process_file(fname, version, symmap)
-    abi_write_vscript(tgt, task.env.LIBNAME, task.env.VERSION, symmap,
-                      task.env.ABI_MATCH)
+    f = open(tgt, mode='w')
+    try:
+        abi_write_vscript(f, task.env.LIBNAME, task.env.VERSION, versions,
+            symmap, task.env.ABI_MATCH)
+    finally:
+        f.close()
 
 
 def ABI_VSCRIPT(bld, libname, abi_directory, version, vscript, abi_match=None):
     '''generate a vscript file for our public libraries'''
     if abi_directory:
-        source = bld.path.ant_glob('%s/%s-[0-9]*.sigs' % (abi_directory, libname))
-        source = sorted(source.split())
+        source = bld.path.ant_glob('%s/%s-[0-9]*.sigs' % (abi_directory, libname), flat=True)
+        def abi_file_key(path):
+            return version_key(path[:-len(".sigs")].rsplit("-")[-1])
+        source = sorted(source.split(), key=abi_file_key)
     else:
         source = ''
 
+    libname = os.path.basename(libname)
+    version = os.path.basename(version)
     libname = libname.replace("-", "_").replace("+","_").upper()
     version = version.replace("-", "_").replace("+","_").upper()