• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2014 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"""Generates the obfuscated jar and test jar for an apk.
8
9If proguard is not enabled or 'Release' is not in the configuration name,
10obfuscation will be a no-op.
11"""
12
13import json
14import optparse
15import os
16import sys
17import tempfile
18
19from util import build_utils
20from util import proguard_util
21
22
23_PROGUARD_KEEP_CLASS = '''-keep class %s {
24  *;
25}
26'''
27
28
29def ParseArgs(argv):
30  parser = optparse.OptionParser()
31  parser.add_option('--android-sdk', help='path to the Android SDK folder')
32  parser.add_option('--android-sdk-tools',
33                    help='path to the Android SDK build tools folder')
34  parser.add_option('--android-sdk-jar',
35                    help='path to Android SDK\'s android.jar')
36  parser.add_option('--proguard-jar-path',
37                    help='Path to proguard.jar in the sdk')
38  parser.add_option('--input-jars-paths',
39                    help='Path to jars to include in obfuscated jar')
40
41  parser.add_option('--proguard-configs',
42                    help='Paths to proguard config files')
43
44  parser.add_option('--configuration-name',
45                    help='Gyp configuration name (i.e. Debug, Release)')
46
47  parser.add_option('--debug-build-proguard-enabled', action='store_true',
48                    help='--proguard-enabled takes effect on release '
49                         'build, this flag enable the proguard on debug '
50                         'build.')
51  parser.add_option('--proguard-enabled', action='store_true',
52                    help='Set if proguard is enabled for this target.')
53
54  parser.add_option('--obfuscated-jar-path',
55                    help='Output path for obfuscated jar.')
56
57  parser.add_option('--testapp', action='store_true',
58                    help='Set this if building an instrumentation test apk')
59  parser.add_option('--tested-apk-obfuscated-jar-path',
60                    help='Path to obfusctated jar of the tested apk')
61  parser.add_option('--test-jar-path',
62                    help='Output path for jar containing all the test apk\'s '
63                    'code.')
64
65  parser.add_option('--stamp', help='File to touch on success')
66
67  parser.add_option('--main-dex-list-path',
68                    help='The list of classes to retain in the main dex. '
69                         'These will not be obfuscated.')
70  parser.add_option('--multidex-configuration-path',
71                    help='A JSON file containing multidex build configuration.')
72  parser.add_option('--verbose', '-v', action='store_true',
73                    help='Print all proguard output')
74
75  (options, args) = parser.parse_args(argv)
76
77  if args:
78    parser.error('No positional arguments should be given. ' + str(args))
79
80  # Check that required options have been provided.
81  required_options = (
82      'android_sdk',
83      'android_sdk_tools',
84      'android_sdk_jar',
85      'proguard_jar_path',
86      'input_jars_paths',
87      'configuration_name',
88      'obfuscated_jar_path',
89      )
90
91  if options.testapp:
92    required_options += (
93        'test_jar_path',
94        )
95
96  build_utils.CheckOptions(options, parser, required=required_options)
97  return options, args
98
99
100def DoProguard(options):
101  proguard = proguard_util.ProguardCmdBuilder(options.proguard_jar_path)
102  proguard.outjar(options.obfuscated_jar_path)
103
104  input_jars = build_utils.ParseGypList(options.input_jars_paths)
105
106  exclude_paths = []
107  configs = build_utils.ParseGypList(options.proguard_configs)
108  if options.tested_apk_obfuscated_jar_path:
109    # configs should only contain the process_resources.py generated config.
110    assert len(configs) == 1, (
111        'test apks should not have custom proguard configs: ' + str(configs))
112    proguard.tested_apk_info(options.tested_apk_obfuscated_jar_path + '.info')
113
114  proguard.libraryjars([options.android_sdk_jar])
115  proguard_injars = [p for p in input_jars if p not in exclude_paths]
116  proguard.injars(proguard_injars)
117
118  multidex_config = _PossibleMultidexConfig(options)
119  if multidex_config:
120    configs.append(multidex_config)
121
122  proguard.configs(configs)
123  proguard.verbose(options.verbose)
124  proguard.CheckOutput()
125
126
127def _PossibleMultidexConfig(options):
128  if not options.multidex_configuration_path:
129    return None
130
131  with open(options.multidex_configuration_path) as multidex_config_file:
132    multidex_config = json.loads(multidex_config_file.read())
133
134  if not (multidex_config.get('enabled') and options.main_dex_list_path):
135    return None
136
137  main_dex_list_config = ''
138  with open(options.main_dex_list_path) as main_dex_list:
139    for clazz in (l.strip() for l in main_dex_list):
140      if clazz.endswith('.class'):
141        clazz = clazz[:-len('.class')]
142      clazz = clazz.replace('/', '.')
143      main_dex_list_config += (_PROGUARD_KEEP_CLASS % clazz)
144  with tempfile.NamedTemporaryFile(
145      delete=False,
146      dir=os.path.dirname(options.main_dex_list_path),
147      prefix='main_dex_list_proguard',
148      suffix='.flags') as main_dex_config_file:
149    main_dex_config_file.write(main_dex_list_config)
150  return main_dex_config_file.name
151
152
153def main(argv):
154  options, _ = ParseArgs(argv)
155
156  input_jars = build_utils.ParseGypList(options.input_jars_paths)
157
158  if options.testapp:
159    dependency_class_filters = [
160        '*R.class', '*R$*.class', '*Manifest.class', '*BuildConfig.class']
161    build_utils.MergeZips(
162        options.test_jar_path, input_jars, dependency_class_filters)
163
164  if ((options.configuration_name == 'Release' and options.proguard_enabled) or
165     (options.configuration_name == 'Debug' and
166      options.debug_build_proguard_enabled)):
167    DoProguard(options)
168  else:
169    output_files = [
170        options.obfuscated_jar_path,
171        options.obfuscated_jar_path + '.info',
172        options.obfuscated_jar_path + '.dump',
173        options.obfuscated_jar_path + '.seeds',
174        options.obfuscated_jar_path + '.usage',
175        options.obfuscated_jar_path + '.mapping']
176    for f in output_files:
177      if os.path.exists(f):
178        os.remove(f)
179      build_utils.Touch(f)
180
181  if options.stamp:
182    build_utils.Touch(options.stamp)
183
184if __name__ == '__main__':
185  sys.exit(main(sys.argv[1:]))
186