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