summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/gyp/create_size_info_files.py
blob: c60b02d7c8e4bae66522ae569521120d76eda3f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python3

# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Creates size-info/*.info files used by SuperSize."""

import argparse
import collections
import os
import re
import sys
import zipfile

from util import build_utils
from util import jar_info_utils


_AAR_VERSION_PATTERN = re.compile(r'/[^/]*?(\.aar/|\.jar/)')


def _RemoveDuplicatesFromList(source_list):
  return collections.OrderedDict.fromkeys(source_list).keys()


def _TransformAarPaths(path):
  # .aar files within //third_party/android_deps have a version suffix.
  # The suffix changes each time .aar files are updated, which makes size diffs
  # hard to compare (since the before/after have different source paths).
  # Rather than changing how android_deps works, we employ this work-around
  # to normalize the paths.
  # From: .../androidx_appcompat_appcompat/appcompat-1.1.0.aar/res/...
  #   To: .../androidx_appcompat_appcompat.aar/res/...
  # https://crbug.com/1056455
  if 'android_deps' not in path:
    return path
  return _AAR_VERSION_PATTERN.sub(r'\1', path)


def _MergeResInfoFiles(res_info_path, info_paths):
  # Concatenate them all.
  # only_if_changed=False since no build rules depend on this as an input.
  with build_utils.AtomicOutput(res_info_path, only_if_changed=False,
                                mode='w+') as dst:
    for p in info_paths:
      with open(p) as src:
        dst.writelines(_TransformAarPaths(l) for l in src)


def _PakInfoPathsForAssets(assets):
  return [f.split(':')[0] + '.info' for f in assets if f.endswith('.pak')]


def _MergePakInfoFiles(merged_path, pak_infos):
  info_lines = set()
  for pak_info_path in pak_infos:
    with open(pak_info_path, 'r') as src_info_file:
      info_lines.update(_TransformAarPaths(x) for x in src_info_file)
  # only_if_changed=False since no build rules depend on this as an input.
  with build_utils.AtomicOutput(merged_path, only_if_changed=False,
                                mode='w+') as f:
    f.writelines(sorted(info_lines))


def _FullJavaNameFromClassFilePath(path):
  # Input:  base/android/java/src/org/chromium/Foo.class
  # Output: base.android.java.src.org.chromium.Foo
  if not path.endswith('.class'):
    return ''
  path = os.path.splitext(path)[0]
  parts = []
  while path:
    # Use split to be platform independent.
    head, tail = os.path.split(path)
    path = head
    parts.append(tail)
  parts.reverse()  # Package comes first
  return '.'.join(parts)


def _MergeJarInfoFiles(output, inputs):
  """Merge several .jar.info files to generate an .apk.jar.info.

  Args:
    output: output file path.
    inputs: List of .jar.info or .jar files.
  """
  info_data = dict()
  for path in inputs:
    # For non-prebuilts: .jar.info files are written by compile_java.py and map
    # .class files to .java source paths.
    #
    # For prebuilts: No .jar.info file exists, we scan the .jar files here and
    # map .class files to the .jar.
    #
    # For .aar files: We look for a "source.info" file in the containing
    # directory in order to map classes back to the .aar (rather than mapping
    # them to the extracted .jar file).
    if path.endswith('.info'):
      info_data.update(jar_info_utils.ParseJarInfoFile(path))
    else:
      attributed_path = path
      if not path.startswith('..'):
        parent_path = os.path.dirname(path)
        # See if it's an sub-jar within the .aar.
        if os.path.basename(parent_path) == 'libs':
          parent_path = os.path.dirname(parent_path)
        aar_source_info_path = os.path.join(parent_path, 'source.info')
        # source.info files exist only for jars from android_aar_prebuilt().
        # E.g. Could have an java_prebuilt() pointing to a generated .jar.
        if os.path.exists(aar_source_info_path):
          attributed_path = jar_info_utils.ReadAarSourceInfo(
              aar_source_info_path)

      with zipfile.ZipFile(path) as zip_info:
        for name in zip_info.namelist():
          fully_qualified_name = _FullJavaNameFromClassFilePath(name)
          if fully_qualified_name:
            info_data[fully_qualified_name] = _TransformAarPaths('{}/{}'.format(
                attributed_path, name))

  # only_if_changed=False since no build rules depend on this as an input.
  with build_utils.AtomicOutput(output, only_if_changed=False) as f:
    jar_info_utils.WriteJarInfoFile(f, info_data)


def _FindJarInputs(jar_paths):
  ret = []
  for jar_path in jar_paths:
    jar_info_path = jar_path + '.info'
    if os.path.exists(jar_info_path):
      ret.append(jar_info_path)
    else:
      ret.append(jar_path)
  return ret


def main(args):
  args = build_utils.ExpandFileArgs(args)
  parser = argparse.ArgumentParser(description=__doc__)
  build_utils.AddDepfileOption(parser)
  parser.add_argument(
      '--jar-info-path', required=True, help='Output .jar.info file')
  parser.add_argument(
      '--pak-info-path', required=True, help='Output .pak.info file')
  parser.add_argument(
      '--res-info-path', required=True, help='Output .res.info file')
  parser.add_argument(
      '--jar-files',
      required=True,
      action='append',
      help='GN-list of .jar file paths')
  parser.add_argument(
      '--assets',
      required=True,
      action='append',
      help='GN-list of files to add as assets in the form '
      '"srcPath:zipPath", where ":zipPath" is optional.')
  parser.add_argument(
      '--uncompressed-assets',
      required=True,
      action='append',
      help='Same as --assets, except disables compression.')
  parser.add_argument(
      '--in-res-info-path',
      required=True,
      action='append',
      help='Paths to .ap_.info files')

  options = parser.parse_args(args)

  options.jar_files = build_utils.ParseGnList(options.jar_files)
  options.assets = build_utils.ParseGnList(options.assets)
  options.uncompressed_assets = build_utils.ParseGnList(
      options.uncompressed_assets)

  jar_inputs = _FindJarInputs(_RemoveDuplicatesFromList(options.jar_files))
  pak_inputs = _PakInfoPathsForAssets(options.assets +
                                      options.uncompressed_assets)
  res_inputs = options.in_res_info_path

  # Just create the info files every time. See https://crbug.com/1045024
  _MergeJarInfoFiles(options.jar_info_path, jar_inputs)
  _MergePakInfoFiles(options.pak_info_path, pak_inputs)
  _MergeResInfoFiles(options.res_info_path, res_inputs)

  all_inputs = jar_inputs + pak_inputs + res_inputs
  build_utils.WriteDepfile(options.depfile,
                           options.jar_info_path,
                           inputs=all_inputs)


if __name__ == '__main__':
  main(sys.argv[1:])