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