toolkit/library/dependentlibs.py
author Ting-Yu Lin <tlin@mozilla.com>
Wed, 24 Apr 2019 22:23:59 +0000
changeset 530044 e5029a42a612c56a826a4634425c9f0670e9a111
parent 513672 53ee4ba4044a392de93114c7a2a05969484d44e3
permissions -rw-r--r--
Bug 1546223 Part 5 - Change ReflowInput::InitConstraints() to use Maybe for containing block size. r=dholbert There are some minor behavior changes come with this. 1) Change the default containing block size to (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE). I think this is more reasonable than (-1, -1). 2) mContainingBlockSize is used to cache only the block size passing though constructor, Init(), or the invalid (-1, -1). This patch makes it cache the value computed by ComputeContainingBlockRectangle(). Note that mContainingBlockSize is used only in nsTableWrapperFrame::InitChildReflowInput() to set the inner table frame's containing block to be the same as the outer table frame's. We don't change this behavior by caching more. Because even if the inner frame use the invalid cached (-1, -1) containing block size from the outer reflow input, it still computes the block size again in InitConstraints(). (Inner table's cb is the same as the outer table's per InitCBReflowInput().) Differential Revision: https://phabricator.services.mozilla.com/D28586

# 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_win32_objdump(lib):
    proc = subprocess.Popen([substs['LLVM_OBJDUMP'], '--private-headers', lib], stdout = subprocess.PIPE)
    deps = []
    for line in proc.stdout:
        match = re.match('\s+DLL 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_mac_objdump(lib):
    '''Returns the list of dependencies declared in the given MACH-O dylib'''
    proc = subprocess.Popen([substs['LLVM_OBJDUMP'], '--private-headers', lib], stdout = subprocess.PIPE)
    deps = []
    cmd = None
    for line in proc.stdout:
        # llvm-objdump --private-headers 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_mac_objdump
    else:
        ext = os.path.splitext(lib)[1]
        assert(ext == '.dll')
        func = dependentlibs_win32_objdump

    deps = dependentlibs(lib, libpaths, func)
    base_lib = mozpath.basename(lib)
    deps[base_lib] = mozpath.join(libpaths[0], base_lib)
    output.write('\n'.join(deps.keys()) + '\n')

    with open(output.name + ".gtest", 'w') as gtest_out:
        libs = deps.keys()
        libs[-1] = 'gtest/' + libs[-1]
        gtest_out.write('\n'.join(libs) + '\n')

    return set(deps.values())

def main():
    gen_list(sys.stdout, sys.argv[1])

if __name__ == '__main__':
    main()