Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 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 optparse |
| 24 | import os |
| 25 | import re |
| 26 | import sys |
| 27 | |
| 28 | from util import build_utils |
| 29 | |
| 30 | _readelf = None |
| 31 | _library_dirs = None |
| 32 | |
| 33 | _library_re = re.compile( |
| 34 | '.*NEEDED.*Shared library: \[(?P<library_name>.+)\]') |
| 35 | |
| 36 | |
| 37 | def SetReadelfPath(path): |
| 38 | global _readelf |
| 39 | _readelf = path |
| 40 | |
| 41 | |
| 42 | def SetLibraryDirs(dirs): |
| 43 | global _library_dirs |
| 44 | _library_dirs = dirs |
| 45 | |
| 46 | |
| 47 | def FullLibraryPath(library_name): |
| 48 | assert _library_dirs is not None |
| 49 | for directory in _library_dirs: |
| 50 | path = '%s/%s' % (directory, library_name) |
| 51 | if os.path.exists(path): |
| 52 | return path |
| 53 | return library_name |
| 54 | |
| 55 | |
| 56 | def IsSystemLibrary(library_name): |
| 57 | # If the library doesn't exist in the libraries directory, assume that it is |
| 58 | # an Android system library. |
| 59 | return not os.path.exists(FullLibraryPath(library_name)) |
| 60 | |
| 61 | |
| 62 | def CallReadElf(library_or_executable): |
| 63 | assert _readelf is not None |
| 64 | readelf_cmd = [_readelf, |
| 65 | '-d', |
| 66 | FullLibraryPath(library_or_executable)] |
| 67 | return build_utils.CheckOutput(readelf_cmd) |
| 68 | |
| 69 | |
| 70 | def GetDependencies(library_or_executable): |
| 71 | elf = CallReadElf(library_or_executable) |
| 72 | return set(_library_re.findall(elf)) |
| 73 | |
| 74 | |
| 75 | def GetNonSystemDependencies(library_name): |
| 76 | all_deps = GetDependencies(library_name) |
| 77 | return set((lib for lib in all_deps if not IsSystemLibrary(lib))) |
| 78 | |
| 79 | |
| 80 | def GetSortedTransitiveDependencies(libraries): |
| 81 | """Returns all transitive library dependencies in dependency order.""" |
| 82 | return build_utils.GetSortedTransitiveDependencies( |
| 83 | libraries, GetNonSystemDependencies) |
| 84 | |
| 85 | |
| 86 | def GetSortedTransitiveDependenciesForBinaries(binaries): |
| 87 | if binaries[0].endswith('.so'): |
| 88 | libraries = [os.path.basename(lib) for lib in binaries] |
| 89 | else: |
| 90 | assert len(binaries) == 1 |
| 91 | all_deps = GetDependencies(binaries[0]) |
| 92 | libraries = [lib for lib in all_deps if not IsSystemLibrary(lib)] |
| 93 | |
| 94 | return GetSortedTransitiveDependencies(libraries) |
| 95 | |
| 96 | |
| 97 | def main(): |
| 98 | parser = optparse.OptionParser() |
| 99 | build_utils.AddDepfileOption(parser) |
| 100 | |
| 101 | parser.add_option('--input-libraries', |
| 102 | help='A list of top-level input libraries.') |
| 103 | parser.add_option('--libraries-dir', |
| 104 | help='The directory which contains shared libraries.') |
| 105 | parser.add_option('--readelf', help='Path to the readelf binary.') |
| 106 | parser.add_option('--output', help='Path to the generated .json file.') |
| 107 | parser.add_option('--stamp', help='Path to touch on success.') |
| 108 | |
| 109 | options, _ = parser.parse_args() |
| 110 | |
| 111 | SetReadelfPath(options.readelf) |
| 112 | SetLibraryDirs(options.libraries_dir.split(',')) |
| 113 | |
| 114 | libraries = build_utils.ParseGypList(options.input_libraries) |
| 115 | if len(libraries): |
| 116 | libraries = GetSortedTransitiveDependenciesForBinaries(libraries) |
| 117 | |
| 118 | # Convert to "base" library names: e.g. libfoo.so -> foo |
| 119 | java_libraries_list = ( |
| 120 | '{%s}' % ','.join(['"%s"' % s[3:-3] for s in libraries])) |
| 121 | |
| 122 | out_json = { |
| 123 | 'libraries': libraries, |
| 124 | 'lib_paths': [FullLibraryPath(l) for l in libraries], |
| 125 | 'java_libraries_list': java_libraries_list |
| 126 | } |
| 127 | build_utils.WriteJson( |
| 128 | out_json, |
| 129 | options.output, |
| 130 | only_if_changed=True) |
| 131 | |
| 132 | if options.stamp: |
| 133 | build_utils.Touch(options.stamp) |
| 134 | |
| 135 | if options.depfile: |
| 136 | build_utils.WriteDepfile( |
| 137 | options.depfile, |
| 138 | libraries + build_utils.GetPythonDependencies()) |
| 139 | |
| 140 | |
| 141 | if __name__ == '__main__': |
| 142 | sys.exit(main()) |
| 143 | |
| 144 | |