From 0f9d9f860918f9063ca98803ae1e55410acbb818 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Wed, 14 Dec 2016 16:30:28 -0800 Subject: Add a builtin repo hook to run google-java-format. Bug: 31552314 Test: Verified hook on a test project; unit test Change-Id: I1e4b2ff5f8285707d0fd3a4a14f65957fa21b273 --- README.md | 4 ++ rh/hooks.py | 18 +++++++++ rh/hooks_unittest.py | 6 +++ tools/google-java-format.py | 94 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100755 tools/google-java-format.py diff --git a/README.md b/README.md index fd3b97f..ff97bb4 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,8 @@ canned hooks already included geared towards AOSP style guidelines. * `commit_msg_test_field`: Require a `Test:` line. * `cpplint`: Run through the cpplint tool (for C++ code). * `gofmt`: Run Go code through `gofmt`. +* `google_java_format`: Run Java code through + [`google-java-format`](https://github.com/google/google-java-format) * `jsonlint`: Verify JSON code is sane. * `pylint`: Run Python code through `pylint`. * `xmllint`: Run XML code through `xmllint`. @@ -193,6 +195,8 @@ distros/versions. The following tools are recognized: * `cpplint`: used for the `cpplint` builtin hook. * `git-clang-format`: used for the `clang_format` builtin hook. * `gofmt`: used for the `gofmt` builtin hook. +* `google-java-format`: used for the `google_java_format` builtin hook. +* `google-java-format-diff`: used for the `google_java_format` builtin hook. * `pylint`: used for the `pylint` builtin hook. See [Placeholders](#Placeholders) for variables you can expand automatically. diff --git a/rh/hooks.py b/rh/hooks.py index 9612bf7..d92c953 100644 --- a/rh/hooks.py +++ b/rh/hooks.py @@ -290,6 +290,21 @@ def check_clang_format(project, commit, _desc, diff, options=None): return _check_cmd('clang-format', project, commit, cmd) +def check_google_java_format(project, commit, _desc, _diff, options=None): + """Run google-java-format on the commit.""" + + if options.args(): + raise ValueError('google-java-format check takes no options') + + tool = get_helper_path('google-java-format.py') + google_java_format = options.tool_path('google-java-format') + google_java_format_diff = options.tool_path('google-java-format-diff') + cmd = [tool, '--google-java-format', google_java_format, + '--google-java-format-diff', google_java_format_diff, + '--commit', commit] + return _check_cmd('google-java-format', project, commit, cmd) + + def check_commit_msg_bug_field(project, commit, desc, _diff, options=None): """Check the commit message for a 'Bug:' line.""" field = 'Bug' @@ -512,6 +527,7 @@ BUILTIN_HOOKS = { 'commit_msg_test_field': check_commit_msg_test_field, 'cpplint': check_cpplint, 'gofmt': check_gofmt, + 'google_java_format': check_google_java_format, 'jsonlint': check_json, 'pylint': check_pylint, 'xmllint': check_xmllint, @@ -524,5 +540,7 @@ TOOL_PATHS = { 'cpplint': os.path.join(TOOLS_DIR, 'cpplint.py'), 'git-clang-format': 'git-clang-format', 'gofmt': 'gofmt', + 'google-java-format': 'google-java-format', + 'google-java-format-diff': 'google-java-format-diff.py', 'pylint': 'pylint', } diff --git a/rh/hooks_unittest.py b/rh/hooks_unittest.py index 0b4ef40..ec4d0b3 100755 --- a/rh/hooks_unittest.py +++ b/rh/hooks_unittest.py @@ -293,6 +293,12 @@ class BuiltinHooksTests(unittest.TestCase): self.project, 'commit', 'desc', (), options=self.options) self.assertEqual(ret, mock_check.return_value) + def test_google_java_format(self, mock_check, _mock_run): + """Verify the google_java_format builtin hook.""" + ret = rh.hooks.check_google_java_format( + self.project, 'commit', 'desc', (), options=self.options) + self.assertEqual(ret, mock_check.return_value) + def test_commit_msg_bug_field(self, _mock_check, _mock_run): """Verify the commit_msg_bug_field builtin hook.""" # Check some good messages. diff --git a/tools/google-java-format.py b/tools/google-java-format.py new file mode 100755 index 0000000..f76175a --- /dev/null +++ b/tools/google-java-format.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +# Copyright 2016 The Android Open Source Project +# +# 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. + +"""Wrapper to run google-java-format to check for any malformatted changes.""" + +from __future__ import print_function + +import argparse +import os +import sys +from distutils.spawn import find_executable + +_path = os.path.realpath(__file__ + '/../..') +if sys.path[0] != _path: + sys.path.insert(0, _path) +del _path + +import rh.shell +import rh.utils + + +def get_parser(): + """Return a command line parser.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--google-java-format', default='google-java-format', + help='The path of the google-java-format executable.') + parser.add_argument('--google-java-format-diff', + default='google-java-format-diff.py', + help='The path of the google-java-format-diff script.') + parser.add_argument('--fix', action='store_true', + help='Fix any formatting errors automatically.') + parser.add_argument('--commit', type=str, default='HEAD', + help='Specify the commit to validate.') + return parser + + +def main(argv): + """The main entry.""" + parser = get_parser() + opts = parser.parse_args(argv) + + # google-java-format-diff.py looks for google-java-format in $PATH, so find + # the parent dir up front and inject it into $PATH when launching it. + # TODO: Pass the path in directly once this issue is resolved: + # https://github.com/google/google-java-format/issues/108 + format_path = find_executable(opts.google_java_format) + if not format_path: + print('Unable to find google-java-format at %s' % + opts.google_java_format) + return 1 + + extra_env = { + 'PATH': '%s%s%s' % (os.path.dirname(format_path), + os.pathsep, + os.environ['PATH']) + } + + # TODO: Delegate to the tool once this issue is resolved: + # https://github.com/google/google-java-format/issues/107 + diff_cmd = ['git', 'diff', '-U0', '%s^!' % opts.commit] + diff = rh.utils.run_command(diff_cmd, capture_output=True).output + + cmd = [opts.google_java_format_diff, '-p1', '--aosp'] + if opts.fix: + cmd.extend(['-i']) + + stdout = rh.utils.run_command(cmd, + input=diff, + capture_output=True, + extra_env=extra_env).output + if stdout != '': + print('One or more files in your commit have Java formatting errors.') + print('You can run `%s --fix %s` to fix this' % + (sys.argv[0], rh.shell.cmd_to_str(argv))) + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) -- cgit v1.2.3