Home | History | Annotate | Download | only in gyp
      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