OLD | NEW |
(Empty) | |
| 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 BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..') |
| 30 sys.path.append(BUILD_ANDROID_DIR) |
| 31 |
| 32 from pylib import build_utils |
| 33 |
| 34 |
| 35 _options = None |
| 36 _libraries_dir = None |
| 37 _library_re = re.compile( |
| 38 '.*NEEDED.*Shared library: \[(?P<library_name>[\w/.]+)\]') |
| 39 |
| 40 |
| 41 def FullLibraryPath(library_name): |
| 42 return '%s/%s' % (_libraries_dir, library_name) |
| 43 |
| 44 |
| 45 def IsSystemLibrary(library_name): |
| 46 # If the library doesn't exist in the libraries directory, assume that it is |
| 47 # an Android system library. |
| 48 return not os.path.exists(FullLibraryPath(library_name)) |
| 49 |
| 50 |
| 51 def CallReadElf(library_name): |
| 52 readelf_cmd = [_options.readelf, |
| 53 '-d', |
| 54 FullLibraryPath(library_name)] |
| 55 return build_utils.CheckCallDie(readelf_cmd) |
| 56 |
| 57 |
| 58 def GetDependencies(library_name): |
| 59 elf = CallReadElf(library_name) |
| 60 return set(_library_re.findall(elf)) |
| 61 |
| 62 |
| 63 def GetNonSystemDependencies(library_name): |
| 64 all_deps = GetDependencies(library_name) |
| 65 return set((lib for lib in all_deps if not IsSystemLibrary(lib))) |
| 66 |
| 67 |
| 68 def GetSortedTransitiveDependencies(libraries): |
| 69 """Returns all transitive library dependencies in dependency order.""" |
| 70 def GraphNode(library): |
| 71 return (library, GetNonSystemDependencies(library)) |
| 72 |
| 73 # First: find all library dependencies. |
| 74 unchecked_deps = libraries |
| 75 all_deps = set(libraries) |
| 76 while unchecked_deps: |
| 77 lib = unchecked_deps.pop() |
| 78 new_deps = GetNonSystemDependencies(lib).difference(all_deps) |
| 79 unchecked_deps.extend(new_deps) |
| 80 all_deps = all_deps.union(new_deps) |
| 81 |
| 82 # Then: simple, slow topological sort. |
| 83 sorted_deps = [] |
| 84 unsorted_deps = dict(map(GraphNode, all_deps)) |
| 85 while unsorted_deps: |
| 86 for library, dependencies in unsorted_deps.items(): |
| 87 if not dependencies.intersection(unsorted_deps.keys()): |
| 88 sorted_deps.append(library) |
| 89 del unsorted_deps[library] |
| 90 |
| 91 return sorted_deps |
| 92 |
| 93 |
| 94 def main(argv): |
| 95 parser = optparse.OptionParser() |
| 96 |
| 97 parser.add_option('--input-libraries', |
| 98 help='A list of top-level input libraries.') |
| 99 parser.add_option('--readelf', help='Path to the readelf binary.') |
| 100 parser.add_option('--output', help='Path to the generated .json file.') |
| 101 parser.add_option('--stamp', help='Path to touch on success.') |
| 102 |
| 103 global _options |
| 104 _options, _ = parser.parse_args() |
| 105 |
| 106 libraries = build_utils.ParseGypList(_options.input_libraries) |
| 107 global _libraries_dir |
| 108 _libraries_dir = os.path.dirname(libraries[0]) |
| 109 libraries = [os.path.basename(lib) for lib in libraries] |
| 110 |
| 111 libraries = GetSortedTransitiveDependencies(libraries) |
| 112 |
| 113 with open(_options.output, 'w') as outfile: |
| 114 json.dump(libraries, outfile) |
| 115 |
| 116 if _options.stamp: |
| 117 build_utils.Touch(_options.stamp) |
| 118 |
| 119 |
| 120 if __name__ == '__main__': |
| 121 sys.exit(main(sys.argv)) |
| 122 |
| 123 |
OLD | NEW |