Currently, we use proguard from two places: proguard.py and apk_obfuscate.py.
These are used to preprocess libraries and to process full apks, respectively.

This extracts construction/running/filtering output of the actual proguard
command to a simple builder class that is then used in both places. This makes
some parts of how proguard is run to be more consistent between the two.

proguard.py now supports running proguard in the way needed for apks. (GN will
be using proguard.py instead of apk_obfuscate.py)

BUG=359249,478319

Review URL: https://codereview.chromium.org/1103013002

Cr-Original-Commit-Position: refs/heads/master@{#328439}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: e852f8d50a4c65f0de6fcc3753abdc8ba3b22a9e
This commit is contained in:
cjhopman 2015-05-05 16:47:15 -07:00 коммит произвёл Commit bot
Родитель 04a29cead4
Коммит c0bc4f0ef1
4 изменённых файлов: 197 добавлений и 98 удалений

Просмотреть файл

@ -15,6 +15,8 @@ import os
import sys
from util import build_utils
from util import proguard_util
def ParseArgs(argv):
parser = optparse.OptionParser()
@ -71,72 +73,60 @@ def ParseArgs(argv):
)
build_utils.CheckOptions(options, parser, required=required_options)
return options, args
def DoProguard(options):
proguard = proguard_util.ProguardCmdBuilder(options.proguard_jar_path)
proguard.outjar(options.obfuscated_jar_path)
library_classpath = [options.android_sdk_jar]
input_jars = build_utils.ParseGypList(options.input_jars_paths)
exclude_paths = []
configs = build_utils.ParseGypList(options.proguard_configs)
if options.tested_apk_obfuscated_jar_path:
# configs should only contain the process_resources.py generated config.
assert len(configs) == 1, (
'test apks should not have custom proguard configs: ' + str(configs))
tested_jar_info = build_utils.ReadJson(
options.tested_apk_obfuscated_jar_path + '.info')
exclude_paths = tested_jar_info['inputs']
configs = tested_jar_info['configs']
proguard.is_test(True)
proguard.mapping(options.tested_apk_obfuscated_jar_path + '.mapping')
library_classpath.append(options.tested_apk_obfuscated_jar_path)
proguard.libraryjars(library_classpath)
proguard_injars = [p for p in input_jars if p not in exclude_paths]
proguard.injars(proguard_injars)
proguard.configs(configs)
proguard.CheckOutput()
this_info = {
'inputs': proguard_injars,
'configs': configs
}
build_utils.WriteJson(
this_info, options.obfuscated_jar_path + '.info')
def main(argv):
options, _ = ParseArgs(argv)
library_classpath = [options.android_sdk_jar]
input_jars = build_utils.ParseGypList(options.input_jars_paths)
dependency_class_filters = [
'*R.class', '*R$*.class', '*Manifest.class', '*BuildConfig.class']
if options.testapp:
dependency_class_filters = [
'*R.class', '*R$*.class', '*Manifest.class', '*BuildConfig.class']
build_utils.MergeZips(
options.test_jar_path, input_jars, dependency_class_filters)
if options.configuration_name == 'Release' and options.proguard_enabled:
proguard_cmd = [
'java', '-jar', options.proguard_jar_path,
'-forceprocessing',
'-libraryjars', ':'.join(library_classpath),
'-dump', options.obfuscated_jar_path + '.dump',
'-printseeds', options.obfuscated_jar_path + '.seeds',
'-printusage', options.obfuscated_jar_path + '.usage',
'-printmapping', options.obfuscated_jar_path + '.mapping',
]
exclude_paths = []
configs = build_utils.ParseGypList(options.proguard_configs)
if (options.tested_apk_obfuscated_jar_path and
options.tested_apk_obfuscated_jar_path != '/'):
# configs should only contain the process_resources.py generated config.
assert len(configs) == 1, (
'test apks should not have custom proguard configs: ' + str(configs))
tested_jar_info = build_utils.ReadJson(
options.tested_apk_obfuscated_jar_path + '.info')
exclude_paths = tested_jar_info['inputs']
configs = tested_jar_info['configs']
proguard_cmd += [
'-dontobfuscate',
'-dontoptimize',
'-dontshrink',
'-dontskipnonpubliclibraryclassmembers',
'-libraryjars', options.tested_apk_obfuscated_jar_path,
'-applymapping', options.tested_apk_obfuscated_jar_path + '.mapping',
]
proguard_injars = [p for p in input_jars if p not in exclude_paths]
proguard_cmd += ['-injars', ':'.join(proguard_injars)]
for config_file in configs:
proguard_cmd += ['-include', config_file]
# The output jar must be specified after inputs.
proguard_cmd += ['-outjars', options.obfuscated_jar_path]
build_utils.CheckOutput(proguard_cmd)
this_info = {
'inputs': proguard_injars,
'configs': configs
}
build_utils.WriteJson(
this_info, options.obfuscated_jar_path + '.info')
DoProguard(options)
else:
output_files = [
options.obfuscated_jar_path,

Просмотреть файл

@ -5,54 +5,32 @@
# found in the LICENSE file.
import optparse
import os
import sys
from util import build_utils
from util import proguard_util
def DoProguard(options):
injars = options.input_path
outjars = options.output_path
proguard = proguard_util.ProguardCmdBuilder(options.proguard_path)
proguard.injars(build_utils.ParseGypList(options.input_paths))
proguard.configs(build_utils.ParseGypList(options.proguard_configs))
proguard.outjar(options.output_path)
if options.mapping:
proguard.mapping(options.mapping)
if options.is_test:
proguard.is_test(True)
classpath = []
for arg in options.classpath:
classpath += build_utils.ParseGypList(arg)
classpath = list(set(classpath))
libraryjars = ':'.join(classpath)
# proguard does its own dependency checking, which can be avoided by deleting
# the output.
if os.path.exists(options.output_path):
os.remove(options.output_path)
proguard_cmd = ['java', '-jar',
options.proguard_path,
'-injars', injars,
'-outjars', outjars,
'-libraryjars', libraryjars,
'@' + options.proguard_config]
build_utils.CheckOutput(proguard_cmd, print_stdout=True,
stdout_filter=FilterProguardOutput)
proguard.libraryjars(classpath)
proguard.CheckOutput()
def FilterProguardOutput(output):
'''ProGuard outputs boring stuff to stdout (proguard version, jar path, etc)
as well as interesting stuff (notes, warnings, etc). If stdout is entirely
boring, this method suppresses the output.
'''
ignore_patterns = [
'ProGuard, version ',
'Reading program jar [',
'Reading library jar [',
'Preparing output jar [',
' Copying resources from program jar [',
]
for line in output.splitlines():
for pattern in ignore_patterns:
if line.startswith(pattern):
break
else:
# line doesn't match any of the patterns; it's probably something worth
# printing out.
return output
return ''
return proguard.GetInputs()
def main(args):
@ -61,23 +39,27 @@ def main(args):
build_utils.AddDepfileOption(parser)
parser.add_option('--proguard-path',
help='Path to the proguard executable.')
parser.add_option('--input-path',
help='Path to the .jar file proguard should run on.')
parser.add_option('--input-paths',
help='Paths to the .jar files proguard should run on.')
parser.add_option('--output-path', help='Path to the generated .jar file.')
parser.add_option('--proguard-config',
help='Path to the proguard configuration file.')
parser.add_option('--proguard-configs',
help='Paths to proguard configuration files.')
parser.add_option('--mapping', help='Path to proguard mapping to apply.')
parser.add_option('--is-test', action='store_true',
help='If true, extra proguard options for instrumentation tests will be '
'added.')
parser.add_option('--classpath', action='append',
help="Classpath for proguard.")
help='Classpath for proguard.')
parser.add_option('--stamp', help='Path to touch on success.')
options, _ = parser.parse_args(args)
DoProguard(options)
inputs = DoProguard(options)
if options.depfile:
build_utils.WriteDepfile(
options.depfile,
build_utils.GetPythonDependencies())
inputs + build_utils.GetPythonDependencies())
if options.stamp:
build_utils.Touch(options.stamp)

Просмотреть файл

@ -0,0 +1,128 @@
# Copyright 2015 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.
import os
from util import build_utils
def FilterProguardOutput(output):
'''ProGuard outputs boring stuff to stdout (proguard version, jar path, etc)
as well as interesting stuff (notes, warnings, etc). If stdout is entirely
boring, this method suppresses the output.
'''
ignore_patterns = [
'ProGuard, version ',
'Reading program jar [',
'Reading library jar [',
'Preparing output jar [',
' Copying resources from program jar [',
]
for line in output.splitlines():
for pattern in ignore_patterns:
if line.startswith(pattern):
break
else:
# line doesn't match any of the patterns; it's probably something worth
# printing out.
return output
return ''
class ProguardCmdBuilder(object):
def __init__(self, proguard_jar):
assert os.path.exists(proguard_jar)
self._proguard_jar_path = proguard_jar
self._test = None
self._mapping = None
self._libraries = None
self._injars = None
self._configs = None
self._outjar = None
def outjar(self, path):
assert self._outjar is None
self._outjar = path
def is_test(self, enable):
assert self._test is None
self._test = enable
def mapping(self, path):
assert self._mapping is None
assert os.path.exists(path), path
self._mapping = path
def libraryjars(self, paths):
assert self._libraries is None
for p in paths:
assert os.path.exists(p), p
self._libraries = paths
def injars(self, paths):
assert self._injars is None
for p in paths:
assert os.path.exists(p), p
self._injars = paths
def configs(self, paths):
assert self._configs is None
for p in paths:
assert os.path.exists(p), p
self._configs = paths
def build(self):
assert self._injars is not None
assert self._outjar is not None
assert self._configs is not None
cmd = [
'java', '-jar', self._proguard_jar_path,
'-forceprocessing',
]
if self._test:
cmd += [
'-dontobfuscate',
'-dontoptimize',
'-dontshrink',
'-dontskipnonpubliclibraryclassmembers',
]
if self._mapping:
cmd += [
'-applymapping', self._mapping,
]
if self._libraries:
cmd += [
'-libraryjars', ':'.join(self._libraries),
]
cmd += [
'-injars', ':'.join(self._injars)
]
for config_file in self._configs:
cmd += ['-include', config_file]
# The output jar must be specified after inputs.
cmd += [
'-outjars', self._outjar,
'-dump', self._outjar + '.dump',
'-printseeds', self._outjar + '.seeds',
'-printusage', self._outjar + '.usage',
'-printmapping', self._outjar + '.mapping',
]
return cmd
def GetInputs(self):
inputs = [self._proguard_jar_path] + self._configs + self._injars
if self._mapping:
inputs.append(self._mapping)
if self._libraries:
inputs += self._libraries
return inputs
def CheckOutput(self):
build_utils.CheckOutput(self.build(), print_stdout=True,
stdout_filter=FilterProguardOutput)

Просмотреть файл

@ -806,7 +806,6 @@
'--input-jars-paths=>(proguard_input_jar_paths)',
'--proguard-configs=>(proguard_flags_paths)',
'--test-jar-path', '<(test_jar_path)',
'--obfuscated-jar-path', '<(obfuscated_jar_path)',