1#!/usr/bin/env python 2# 3# Copyright 2013 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Writes dependency ordered list of native libraries. 8 9The list excludes any Android system libraries, as those are not bundled with 10the APK. 11 12This list of libraries is used for several steps of building an APK. 13In the component build, the --input-libraries only needs to be the top-level 14library (i.e. libcontent_shell_content_view). This will then use readelf to 15inspect the shared libraries and determine the full list of (non-system) 16libraries that should be included in the APK. 17""" 18 19# TODO(cjhopman): See if we can expose the list of library dependencies from 20# gyp, rather than calculating it ourselves. 21# http://crbug.com/225558 22 23import optparse 24import os 25import re 26import sys 27 28from util import build_utils 29 30_options = None 31_library_re = re.compile( 32 '.*NEEDED.*Shared library: \[(?P<library_name>[\w/.]+)\]') 33 34 35def FullLibraryPath(library_name): 36 return '%s/%s' % (_options.libraries_dir, library_name) 37 38 39def IsSystemLibrary(library_name): 40 # If the library doesn't exist in the libraries directory, assume that it is 41 # an Android system library. 42 return not os.path.exists(FullLibraryPath(library_name)) 43 44 45def CallReadElf(library_or_executable): 46 readelf_cmd = [_options.readelf, 47 '-d', 48 library_or_executable] 49 return build_utils.CheckOutput(readelf_cmd) 50 51 52def GetDependencies(library_or_executable): 53 elf = CallReadElf(library_or_executable) 54 return set(_library_re.findall(elf)) 55 56 57def GetNonSystemDependencies(library_name): 58 all_deps = GetDependencies(FullLibraryPath(library_name)) 59 return set((lib for lib in all_deps if not IsSystemLibrary(lib))) 60 61 62def GetSortedTransitiveDependencies(libraries): 63 """Returns all transitive library dependencies in dependency order.""" 64 def GraphNode(library): 65 return (library, GetNonSystemDependencies(library)) 66 67 # First: find all library dependencies. 68 unchecked_deps = libraries 69 all_deps = set(libraries) 70 while unchecked_deps: 71 lib = unchecked_deps.pop() 72 new_deps = GetNonSystemDependencies(lib).difference(all_deps) 73 unchecked_deps.extend(new_deps) 74 all_deps = all_deps.union(new_deps) 75 76 # Then: simple, slow topological sort. 77 sorted_deps = [] 78 unsorted_deps = dict(map(GraphNode, all_deps)) 79 while unsorted_deps: 80 for library, dependencies in unsorted_deps.items(): 81 if not dependencies.intersection(unsorted_deps.keys()): 82 sorted_deps.append(library) 83 del unsorted_deps[library] 84 85 return sorted_deps 86 87def GetSortedTransitiveDependenciesForExecutable(executable): 88 """Returns all transitive library dependencies in dependency order.""" 89 all_deps = GetDependencies(executable) 90 libraries = [lib for lib in all_deps if not IsSystemLibrary(lib)] 91 return GetSortedTransitiveDependencies(libraries) 92 93 94def main(): 95 parser = optparse.OptionParser() 96 97 parser.add_option('--input-libraries', 98 help='A list of top-level input libraries.') 99 parser.add_option('--libraries-dir', 100 help='The directory which contains shared libraries.') 101 parser.add_option('--readelf', help='Path to the readelf binary.') 102 parser.add_option('--output', help='Path to the generated .json file.') 103 parser.add_option('--stamp', help='Path to touch on success.') 104 105 global _options 106 _options, _ = parser.parse_args() 107 108 libraries = build_utils.ParseGypList(_options.input_libraries) 109 if libraries[0].endswith('.so'): 110 libraries = [os.path.basename(lib) for lib in libraries] 111 libraries = GetSortedTransitiveDependencies(libraries) 112 else: 113 libraries = GetSortedTransitiveDependenciesForExecutable(libraries[0]) 114 115 build_utils.WriteJson(libraries, _options.output, only_if_changed=True) 116 117 if _options.stamp: 118 build_utils.Touch(_options.stamp) 119 120 121if __name__ == '__main__': 122 sys.exit(main()) 123 124 125