diff options
Diffstat (limited to 'toolkit/library/dependentlibs.py')
-rw-r--r-- | toolkit/library/dependentlibs.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/toolkit/library/dependentlibs.py b/toolkit/library/dependentlibs.py new file mode 100644 index 000000000..f2135d7c3 --- /dev/null +++ b/toolkit/library/dependentlibs.py @@ -0,0 +1,139 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +'''Given a library, dependentlibs.py prints the list of libraries it depends +upon that are in the same directory, followed by the library itself. +''' + +import os +import re +import subprocess +import sys +import mozpack.path as mozpath +from collections import OrderedDict +from mozpack.executables import ( + get_type, + ELF, + MACHO, +) +from buildconfig import substs + +def dependentlibs_dumpbin(lib): + '''Returns the list of dependencies declared in the given DLL''' + try: + proc = subprocess.Popen(['dumpbin', '-dependents', lib], stdout = subprocess.PIPE) + except OSError: + # dumpbin is missing, probably mingw compilation. Try using objdump. + return dependentlibs_mingw_objdump(lib) + deps = [] + for line in proc.stdout: + # Each line containing an imported library name starts with 4 spaces + match = re.match(' (\S+)', line) + if match: + deps.append(match.group(1)) + elif len(deps): + # There may be several groups of library names, but only the + # first one is interesting. The second one is for delayload-ed + # libraries. + break + proc.wait() + return deps + +def dependentlibs_mingw_objdump(lib): + proc = subprocess.Popen(['objdump', '-x', lib], stdout = subprocess.PIPE) + deps = [] + for line in proc.stdout: + match = re.match('\tDLL Name: (\S+)', line) + if match: + deps.append(match.group(1)) + proc.wait() + return deps + +def dependentlibs_readelf(lib): + '''Returns the list of dependencies declared in the given ELF .so''' + proc = subprocess.Popen([substs.get('TOOLCHAIN_PREFIX', '') + 'readelf', '-d', lib], stdout = subprocess.PIPE) + deps = [] + for line in proc.stdout: + # Each line has the following format: + # tag (TYPE) value + # or with BSD readelf: + # tag TYPE value + # Looking for NEEDED type entries + tmp = line.split(' ', 3) + if len(tmp) > 3 and 'NEEDED' in tmp[2]: + # NEEDED lines look like: + # 0x00000001 (NEEDED) Shared library: [libname] + # or with BSD readelf: + # 0x00000001 NEEDED Shared library: [libname] + match = re.search('\[(.*)\]', tmp[3]) + if match: + deps.append(match.group(1)) + proc.wait() + return deps + +def dependentlibs_otool(lib): + '''Returns the list of dependencies declared in the given MACH-O dylib''' + proc = subprocess.Popen([substs['OTOOL'], '-l', lib], stdout = subprocess.PIPE) + deps= [] + cmd = None + for line in proc.stdout: + # otool -l output contains many different things. The interesting data + # is under "Load command n" sections, with the content: + # cmd LC_LOAD_DYLIB + # cmdsize 56 + # name libname (offset 24) + tmp = line.split() + if len(tmp) < 2: + continue + if tmp[0] == 'cmd': + cmd = tmp[1] + elif cmd == 'LC_LOAD_DYLIB' and tmp[0] == 'name': + deps.append(re.sub('^@executable_path/','',tmp[1])) + proc.wait() + return deps + +def dependentlibs(lib, libpaths, func): + '''For a given library, returns the list of recursive dependencies that can + be found in the given list of paths, followed by the library itself.''' + assert(libpaths) + assert(isinstance(libpaths, list)) + deps = OrderedDict() + for dep in func(lib): + if dep in deps or os.path.isabs(dep): + continue + for dir in libpaths: + deppath = os.path.join(dir, dep) + if os.path.exists(deppath): + deps.update(dependentlibs(deppath, libpaths, func)) + # Black list the ICU data DLL because preloading it at startup + # leads to startup performance problems because of its excessive + # size (around 10MB). + if not dep.startswith("icu"): + deps[dep] = deppath + break + + return deps + +def gen_list(output, lib): + libpaths = [os.path.join(substs['DIST'], 'bin')] + binary_type = get_type(lib) + if binary_type == ELF: + func = dependentlibs_readelf + elif binary_type == MACHO: + func = dependentlibs_otool + else: + ext = os.path.splitext(lib)[1] + assert(ext == '.dll') + func = dependentlibs_dumpbin + + deps = dependentlibs(lib, libpaths, func) + deps[lib] = mozpath.join(libpaths[0], lib) + output.write('\n'.join(deps.keys()) + '\n') + return set(deps.values()) + +def main(): + gen_list(sys.stdout, sys.argv[1]) + +if __name__ == '__main__': + main() |