samba_abi.py: avoid inefficient string concatenations
[samba.git] / buildtools / wafsamba / symbols.py
index dfab71c2c4323ec583ee07f520d92cfb12e54c9e..3eca3d46bd71cf0780b3c8e36a26b96bd3aa3b83 100644 (file)
@@ -1,9 +1,10 @@
 # a waf tool to extract symbols from object files or libraries
 # using nm, producing a set of exposed defined/undefined symbols
 
-import Utils, Build, subprocess, Logs, re
-from samba_wildcard import fake_build_environment
-from samba_utils import *
+import os, re, subprocess
+from waflib import Utils, Build, Options, Logs, Errors
+from waflib.Logs import debug
+from samba_utils import TO_LIST, LOCAL_CACHE, get_tgt_list, os_path_relpath
 
 # these are the data structures used in symbols.py:
 #
@@ -58,12 +59,12 @@ def symbols_extract(bld, objfiles, dynamic=False):
 
     for line in nmpipe:
         line = line.strip()
-        if line.endswith(':'):
+        if line.endswith(b':'):
             filename = line[:-1]
             ret[filename] = { "PUBLIC": set(), "UNDEFINED" : set() }
             continue
-        cols = line.split(" ")
-        if cols == ['']:
+        cols = line.split(b" ")
+        if cols == [b'']:
             continue
         # see if the line starts with an address
         if len(cols) == 3:
@@ -72,10 +73,10 @@ def symbols_extract(bld, objfiles, dynamic=False):
         else:
             symbol_type = cols[0]
             symbol = cols[1]
-        if symbol_type in "BDGTRVWSi":
+        if symbol_type in b"BDGTRVWSi":
             # its a public symbol
             ret[filename]["PUBLIC"].add(symbol)
-        elif symbol_type in "U":
+        elif symbol_type in b"U":
             ret[filename]["UNDEFINED"].add(symbol)
 
     # add to the cache
@@ -105,10 +106,10 @@ def find_ldd_path(bld, libname, binary):
     lddpipe = subprocess.Popen(['ldd', binary], stdout=subprocess.PIPE).stdout
     for line in lddpipe:
         line = line.strip()
-        cols = line.split(" ")
-        if len(cols) < 3 or cols[1] != "=>":
+        cols = line.split(b" ")
+        if len(cols) < 3 or cols[1] != b"=>":
             continue
-        if cols[0].startswith("libc."):
+        if cols[0].startswith(b"libc."):
             # save this one too
             bld.env.libc_path = cols[2]
         if cols[0].startswith(libname):
@@ -118,8 +119,9 @@ def find_ldd_path(bld, libname, binary):
 
 
 # some regular expressions for parsing readelf output
-re_sharedlib = re.compile('Shared library: \[(.*)\]')
-re_rpath     = re.compile('Library rpath: \[(.*)\]')
+re_sharedlib = re.compile(b'Shared library: \[(.*)\]')
+# output from readelf could be `Library rpath` or `Libray runpath`
+re_rpath     = re.compile(b'Library (rpath|runpath): \[(.*)\]')
 
 def get_libs(bld, binname):
     '''find the list of linked libraries for any binary or library
@@ -145,7 +147,8 @@ def get_libs(bld, binname):
             libs.add(m.group(1))
         m = re_rpath.search(line)
         if m:
-            rpath.extend(m.group(1).split(":"))
+            # output from Popen is always bytestr even in py3
+            rpath.extend(m.group(2).split(b":"))
 
     ret = set()
     for lib in libs:
@@ -251,7 +254,7 @@ def build_symbol_sets(bld, tgt_list):
             bld.env.public_symbols[name] = t.public_symbols
         if t.samba_type == 'LIBRARY':
             for dep in t.add_objects:
-                t2 = bld.name_to_obj(dep, bld.env)
+                t2 = bld.get_tgen_by_name(dep)
                 bld.ASSERT(t2 is not None, "Library '%s' has unknown dependency '%s'" % (name, dep))
                 bld.env.public_symbols[name] = bld.env.public_symbols[name].union(t2.public_symbols)
 
@@ -264,7 +267,7 @@ def build_symbol_sets(bld, tgt_list):
             bld.env.used_symbols[name] = t.used_symbols
         if t.samba_type == 'LIBRARY':
             for dep in t.add_objects:
-                t2 = bld.name_to_obj(dep, bld.env)
+                t2 = bld.get_tgen_by_name(dep)
                 bld.ASSERT(t2 is not None, "Library '%s' has unknown dependency '%s'" % (name, dep))
                 bld.env.used_symbols[name] = bld.env.used_symbols[name].union(t2.used_symbols)
 
@@ -362,7 +365,7 @@ def build_autodeps(bld, t):
             if targets[depname[0]] in [ 'SYSLIB' ]:
                 deps.add(depname[0])
                 continue
-            t2 = bld.name_to_obj(depname[0], bld.env)
+            t2 = bld.get_tgen_by_name(depname[0])
             if len(t2.in_library) != 1:
                 deps.add(depname[0])
                 continue
@@ -385,7 +388,7 @@ def build_library_names(bld, tgt_list):
     for t in tgt_list:
         if t.samba_type in [ 'LIBRARY' ]:
             for obj in t.samba_deps_extended:
-                t2 = bld.name_to_obj(obj, bld.env)
+                t2 = bld.get_tgen_by_name(obj)
                 if t2 and t2.samba_type in [ 'SUBSYSTEM', 'ASN1' ]:
                     if not t.sname in t2.in_library:
                         t2.in_library.append(t.sname)
@@ -402,14 +405,14 @@ def check_library_deps(bld, t):
         Logs.warn("WARNING: Target '%s' in multiple libraries: %s" % (t.sname, t.in_library))
 
     for dep in t.autodeps:
-        t2 = bld.name_to_obj(dep, bld.env)
+        t2 = bld.get_tgen_by_name(dep)
         if t2 is None:
             continue
         for dep2 in t2.autodeps:
             if dep2 == name and t.in_library != t2.in_library:
                 Logs.warn("WARNING: mutual dependency %s <=> %s" % (name, real_name(t2.sname)))
                 Logs.warn("Libraries should match. %s != %s" % (t.in_library, t2.in_library))
-                # raise Utils.WafError("illegal mutual dependency")
+                # raise Errors.WafError("illegal mutual dependency")
 
 
 def check_syslib_collisions(bld, tgt_list):
@@ -429,13 +432,13 @@ def check_syslib_collisions(bld, tgt_list):
                 Logs.error("ERROR: Target '%s' has symbols '%s' which is also in syslib '%s'" % (t.sname, common, lib))
                 has_error = True
     if has_error:
-        raise Utils.WafError("symbols in common with system libraries")
+        raise Errors.WafError("symbols in common with system libraries")
 
 
 def check_dependencies(bld, t):
     '''check for depenencies that should be changed'''
 
-    if bld.name_to_obj(t.sname + ".objlist", bld.env):
+    if bld.get_tgen_by_name(t.sname + ".objlist"):
         return
 
     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
@@ -475,7 +478,7 @@ def check_dependencies(bld, t):
 def check_syslib_dependencies(bld, t):
     '''check for syslib depenencies'''
 
-    if bld.name_to_obj(t.sname + ".objlist", bld.env):
+    if bld.get_tgen_by_name(t.sname + ".objlist"):
         return
 
     sname = real_name(t.sname)
@@ -545,7 +548,7 @@ def symbols_whyneeded(task):
 
     why = Options.options.WHYNEEDED.split(":")
     if len(why) != 2:
-        raise Utils.WafError("usage: WHYNEEDED=TARGET:DEPENDENCY")
+        raise Errors.WafError("usage: WHYNEEDED=TARGET:DEPENDENCY")
     target = why[0]
     subsystem = why[1]
 
@@ -567,9 +570,9 @@ def symbols_whyneeded(task):
         Logs.info("target '%s' uses symbols %s from '%s'" % (target, overlap, subsystem))
 
 
-def report_duplicate(bld, binname, sym, libs):
+def report_duplicate(bld, binname, sym, libs, fail_on_error):
     '''report duplicated symbols'''
-    if sym in ['_init', '_fini']:
+    if sym in ['_init', '_fini', '_edata', '_end', '__bss_start']:
         return
     libnames = []
     for lib in libs:
@@ -577,10 +580,13 @@ def report_duplicate(bld, binname, sym, libs):
             libnames.append(bld.env.library_dict[lib])
         else:
             libnames.append(lib)
-    print("%s: Symbol %s linked in multiple libraries %s" % (binname, sym, libnames))
+    if fail_on_error:
+        raise Errors.WafError("%s: Symbol %s linked in multiple libraries %s" % (binname, sym, libnames))
+    else:
+        print("%s: Symbol %s linked in multiple libraries %s" % (binname, sym, libnames))
 
 
-def symbols_dupcheck_binary(bld, binname):
+def symbols_dupcheck_binary(bld, binname, fail_on_error):
     '''check for duplicated symbols in one binary'''
 
     libs = get_libs_recursive(bld, binname, set())
@@ -589,6 +595,8 @@ def symbols_dupcheck_binary(bld, binname):
     symmap = {}
     for libpath in symlist:
         for sym in symlist[libpath]['PUBLIC']:
+            if sym == '_GLOBAL_OFFSET_TABLE_':
+                continue
             if not sym in symmap:
                 symmap[sym] = set()
             symmap[sym].add(libpath)
@@ -596,10 +604,10 @@ def symbols_dupcheck_binary(bld, binname):
         if len(symmap[sym]) > 1:
             for libpath in symmap[sym]:
                 if libpath in bld.env.library_dict:
-                    report_duplicate(bld, binname, sym, symmap[sym])
+                    report_duplicate(bld, binname, sym, symmap[sym], fail_on_error)
                     break
 
-def symbols_dupcheck(task):
+def symbols_dupcheck(task, fail_on_error=False):
     '''check for symbols defined in two different subsystems'''
     bld = task.env.bld
     tgt_list = get_tgt_list(bld)
@@ -610,9 +618,13 @@ def symbols_dupcheck(task):
     for t in tgt_list:
         if t.samba_type == 'BINARY':
             binname = os_path_relpath(t.link_task.outputs[0].abspath(bld.env), os.getcwd())
-            symbols_dupcheck_binary(bld, binname)
+            symbols_dupcheck_binary(bld, binname, fail_on_error)
 
 
+def symbols_dupcheck_fatal(task):
+    '''check for symbols defined in two different subsystems (and fail if duplicates are found)'''
+    symbols_dupcheck(task, fail_on_error=True)
+
 
 def SYMBOL_CHECK(bld):
     '''check our dependency lists'''
@@ -636,3 +648,12 @@ def SYMBOL_CHECK(bld):
 
 
 Build.BuildContext.SYMBOL_CHECK = SYMBOL_CHECK
+
+def DUP_SYMBOL_CHECK(bld):
+    if Options.options.DUP_SYMBOLCHECK and bld.env.DEVELOPER:
+        '''check for duplicate symbols'''
+        bld.SET_BUILD_GROUP('syslibcheck')
+        task = bld(rule=symbols_dupcheck_fatal, always=True, name='symbol duplicate checking')
+        task.env.bld = bld
+
+Build.BuildContext.DUP_SYMBOL_CHECK = DUP_SYMBOL_CHECK