diff options
Diffstat (limited to 'tools/jdk')
-rw-r--r-- | tools/jdk/BUILD | 26 | ||||
-rw-r--r-- | tools/jdk/proguard_whitelister.py | 87 | ||||
-rw-r--r-- | tools/jdk/proguard_whitelister_test.py | 73 | ||||
-rw-r--r-- | tools/jdk/proguard_whitelister_test_input.cfg | 48 |
4 files changed, 233 insertions, 1 deletions
diff --git a/tools/jdk/BUILD b/tools/jdk/BUILD index 3326081607..b8f04197eb 100644 --- a/tools/jdk/BUILD +++ b/tools/jdk/BUILD @@ -103,10 +103,34 @@ exports_files([ filegroup( name = "srcs", - srcs = ["BUILD"], # Tools are build from the workspace for tests. + srcs = [ + "BUILD", # Tools are build from the workspace for tests. + "proguard_whitelister.py", + "proguard_whitelister_test.py", + "proguard_whitelister_test_input.cfg", + ], ) filegroup( name = "package-srcs", srcs = glob(["**"]), ) + +py_binary( + name = "proguard_whitelister", + srcs = [ + "proguard_whitelister.py", + ], + deps = [ + "//third_party/py/gflags", + ], +) + +py_test( + name = "proguard_whitelister_test", + srcs = ["proguard_whitelister_test.py"], + data = ["proguard_whitelister_test_input.cfg"], + deps = [ + ":proguard_whitelister", + ], +) diff --git a/tools/jdk/proguard_whitelister.py b/tools/jdk/proguard_whitelister.py new file mode 100644 index 0000000000..6c95360df7 --- /dev/null +++ b/tools/jdk/proguard_whitelister.py @@ -0,0 +1,87 @@ +# Copyright 2015 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Checks for proguard configuration rules that cannot be combined across libs. + +The only valid proguard arguments for a library are -keep, -assumenosideeffects, +and -dontnote and -dontwarn when they are provided with arguments. +Limiting libraries to using these flags prevents drastic, sweeping effects +(such as obfuscation being disabled) from being inadvertently applied to a +binary through a library dependency. +""" + +import re +import sys + +from third_party.py import gflags + +gflags.DEFINE_string('path', None, 'Path to the proguard config to validate') +gflags.DEFINE_string('output', None, 'Where to put the validated config') + +FLAGS = gflags.FLAGS +PROGUARD_COMMENTS_PATTERN = '#.*(\n|$)' + + +class ProguardConfigValidator(object): + """Validates a proguard config.""" + + # Must be a tuple for str.startswith() + _VALID_ARGS = ('keep', 'assumenosideeffects') + + def __init__(self, config_path, outconfig_path): + self._config_path = config_path + self._outconfig_path = outconfig_path + + def ValidateAndWriteOutput(self): + with open(self._config_path) as config: + config_string = config.read() + invalid_configs = self._Validate(config_string) + if invalid_configs: + raise RuntimeError( + 'Invalid proguard config parameters: ' + str(invalid_configs)) + with open(self._outconfig_path, 'w+') as outconfig: + config_string = '# Merged from %s \n%s' % ( + self._config_path, config_string) + outconfig.write(config_string) + + def _Validate(self, config): + """Checks the config for illegal arguments.""" + config = re.sub(PROGUARD_COMMENTS_PATTERN, '', config) + args = config.split('-') + invalid_configs = [] + for arg in args: + arg = arg.strip() + if not arg or self._ValidateArg(arg): + continue + invalid_configs.append('-' + arg.split()[0]) + + return invalid_configs + + def _ValidateArg(self, arg): + if arg.startswith(ProguardConfigValidator._VALID_ARGS): + return True + elif arg.split()[0] in ['dontnote', 'dontwarn']: + if len(arg.split()) > 1: + return True + return False + + +def main(): + validator = ProguardConfigValidator(FLAGS.path, FLAGS.output) + validator.ValidateAndWriteOutput() + + +if __name__ == '__main__': + FLAGS(sys.argv) + main() diff --git a/tools/jdk/proguard_whitelister_test.py b/tools/jdk/proguard_whitelister_test.py new file mode 100644 index 0000000000..6a24501fbe --- /dev/null +++ b/tools/jdk/proguard_whitelister_test.py @@ -0,0 +1,73 @@ +# Copyright 2015 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import unittest + +from tools.jdk import proguard_whitelister + + +class ProguardConfigValidatorTest(unittest.TestCase): + + def _CreateValidator(self, input_path, output_path): + return proguard_whitelister.ProguardConfigValidator(input_path, output_path) + + def testValidConfig(self): + input_path = os.path.join( + os.path.dirname(__file__), "proguard_whitelister_test_input.cfg") + tmpdir = os.environ["TEST_TMPDIR"] + output_path = os.path.join(tmpdir, "proguard_whitelister_test_output.cfg") + # This will raise an exception if the config is invalid. + self._CreateValidator(input_path, output_path).ValidateAndWriteOutput() + with file(output_path) as output: + self.assertTrue(("# Merged from %s" % input_path) in output.read()) + + def _TestInvalidConfig(self, invalid_args, config): + tmpdir = os.environ["TEST_TMPDIR"] + input_path = os.path.join(tmpdir, "proguard_whitelister_test_input.cfg") + with open(input_path, "w") as f: + f.write(config) + output_path = os.path.join(tmpdir, "proguard_whitelister_test_output.cfg") + validator = self._CreateValidator(input_path, output_path) + try: + validator.ValidateAndWriteOutput() + self.fail() + except RuntimeError as e: + for invalid_arg in invalid_args: + self.assertTrue(invalid_arg in str(e)) + + def testInvalidNoteConfig(self): + self._TestInvalidConfig(["-dontnote"], """\ +# We don"t want libraries disabling notes globally. +-dontnote""") + + def testInvalidWarnConfig(self): + self._TestInvalidConfig(["-dontwarn"], """\ +# We don"t want libraries disabling warnings globally. +-dontwarn""") + + def testInvalidOptimizationConfig(self): + self._TestInvalidConfig(["-optimizations"], """\ +# We don"t want libraries disabling global optimizations. +-optimizations !class/merging/*,!code/allocation/variable""") + + def testMultipleInvalidArgs(self): + self._TestInvalidConfig(["-optimizations", "-dontnote"], """\ +# We don"t want libraries disabling global optimizations. +-optimizations !class/merging/*,!code/allocation/variable +-dontnote""") + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/jdk/proguard_whitelister_test_input.cfg b/tools/jdk/proguard_whitelister_test_input.cfg new file mode 100644 index 0000000000..ceb3709dd8 --- /dev/null +++ b/tools/jdk/proguard_whitelister_test_input.cfg @@ -0,0 +1,48 @@ +# Copyright 2015 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Note: put any environment specific flags in their respective proguard files: +# proguard_test.flags, proguard_dev.flags, proguard_release.flags, etc + +# These classes reference resources that don't exist in pre-v11 builds +-dontwarn com.google.android.apps.testapp.TestActivity + +# References to a hidden class +-dontnote android.os.SystemProperties +-keep class android.os.SystemProperties { *** get(...);} + +# Keep all classes extended from com.google.android.apps.testapp.MyBaseClass +# because, e.g., we use reflection. +-keep class * extends com.google.android.apps.testapp.MyBaseClass { + @com.google.android.apps.testapp.MyBaseClass$Inner <fields>; +} + +# Needed because this field is accessed reflectively, and it exists in generated code. +-keepclassmembers class * extends com.google.protobuf.nano.MessageNano { + *** apiHeader; +} + +# Extensions are deserialized with a reflective call to newInstance(). +-keepclassmembers class * extends com.google.protobuf.nano.Extension { + <init>(); +} + +-dontnote android.support.v?.app.Fragment + +-keepnames class com.google.android.testapp.** extends com.google.android.testapp.resources.Resource { *; } + +-keepclasseswithmembers class derp.foo { bar;} -keepattributes * + +# This is a comment, so this should not cause problems -dontobfuscate +# This is a comment, so # this should not cause problems -dontnote |