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
9The list excludes any Android system libraries, as those are not bundled with
10the APK.
11
12This list of libraries is used for several steps of building an APK.
13In the component build, the --input-libraries only needs to be the top-level
14library (i.e. libcontent_shell_content_view). This will then use readelf to
15inspect the shared libraries and determine the full list of (non-system)
16libraries 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
23import optparse
24import os
25import re
26import sys
27
28from 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>[\w/.]+)\]')
35
36
37def SetReadelfPath(path):
38  global _readelf
39  _readelf = path
40
41
42def SetLibraryDirs(dirs):
43  global _library_dirs
44  _library_dirs = dirs
45
46
47def 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
56def 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
62def 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
70def GetDependencies(library_or_executable):
71  elf = CallReadElf(library_or_executable)
72  return set(_library_re.findall(elf))
73
74
75def GetNonSystemDependencies(library_name):
76  all_deps = GetDependencies(FullLibraryPath(library_name))
77  return set((lib for lib in all_deps if not IsSystemLibrary(lib)))
78
79
80def GetSortedTransitiveDependencies(libraries):
81  """Returns all transitive library dependencies in dependency order."""
82  return build_utils.GetSortedTransitiveDependencies(
83      libraries, GetNonSystemDependencies)
84
85
86def 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
97def 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  build_utils.WriteJson(
123      {'libraries': libraries, 'java_libraries_list': java_libraries_list},
124      options.output,
125      only_if_changed=True)
126
127  if options.stamp:
128    build_utils.Touch(options.stamp)
129
130  if options.depfile:
131    print libraries
132    build_utils.WriteDepfile(
133        options.depfile,
134        libraries + build_utils.GetPythonDependencies())
135
136
137if __name__ == '__main__':
138  sys.exit(main())
139
140
141