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