blob: afb2b3e516b1b956c72698cc9bb017d14fba67b7 [file] [log] [blame]
#!/usr/bin/python
#
# Copyright 2015, 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.
#
"""Script that is used by developers to run style checks on Java files."""
import argparse
import errno
import os
import subprocess
import sys
import xml.dom.minidom
import gitlint.git as git
MAIN_DIRECTORY = os.path.normpath(os.path.dirname(__file__))
CHECKSTYLE_JAR = os.path.join(MAIN_DIRECTORY, 'checkstyle.jar')
CHECKSTYLE_STYLE = os.path.join(MAIN_DIRECTORY, 'android-style.xml')
FORCED_RULES = ['com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck',
'com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck']
def RunCheckstyle(java_files):
if not os.path.exists(CHECKSTYLE_STYLE):
print 'Java checkstyle configuration file is missing'
sys.exit(1)
if java_files:
# Files to check were specified via command line.
print 'Running Checkstyle on inputted files'
sha = ''
last_commit_modified_files = {}
java_files = map(os.path.abspath, java_files)
else:
# Files where not specified exclicitly. Let's use last commit files.
sha, last_commit_modified_files = _GetModifiedFiles()
print 'Running Checkstyle on %s commit' % sha
java_files = last_commit_modified_files.keys()
if not java_files:
print 'No files to check'
return
stdout = _ExecuteCheckstyle(java_files)
(errors, warnings) = _ParseAndFilterOutput(stdout,
sha,
last_commit_modified_files)
if errors:
print 'ERRORS:'
print '\n'.join(errors)
if warnings:
print 'WARNINGS:'
print '\n'.join(warnings)
if errors or warnings:
sys.exit(1)
print 'SUCCESS! NO ISSUES FOUND'
sys.exit(0)
def _ExecuteCheckstyle(java_files):
# Run checkstyle
checkstyle_env = os.environ.copy()
checkstyle_env['JAVA_CMD'] = 'java'
try:
check = subprocess.Popen(['java', '-cp',
CHECKSTYLE_JAR,
'com.puppycrawl.tools.checkstyle.Main', '-c',
CHECKSTYLE_STYLE, '-f', 'xml'] + java_files,
stdout=subprocess.PIPE, env=checkstyle_env)
stdout, _ = check.communicate()
except OSError as e:
if e.errno == errno.ENOENT:
print 'Error running Checkstyle!'
sys.exit(1)
# Work-around for Checkstyle printing error count to stdio.
if 'Checkstyle ends with' in stdout.splitlines()[-1]:
stdout = '\n'.join(stdout.splitlines()[:-1])
return stdout
def _ParseAndFilterOutput(stdout, sha, last_commit_modified_files):
result_errors = []
result_warnings = []
root = xml.dom.minidom.parseString(stdout)
for file_element in root.getElementsByTagName('file'):
file_name = file_element.attributes['name'].value
if last_commit_modified_files:
modified_lines = git.modified_lines(file_name,
last_commit_modified_files[file_name],
sha)
file_name = os.path.relpath(file_name)
errors = file_element.getElementsByTagName('error')
for error in errors:
line = error.attributes['line'].value
if last_commit_modified_files and int(line) not in modified_lines:
if error.attributes['source'].value not in FORCED_RULES:
continue
column = ''
if error.hasAttribute('column'):
column = '%s:' % (error.attributes['column'].value)
message = error.attributes['message'].value
result = ' %s:%s:%s %s' % (file_name, line, column, message)
severity = error.attributes['severity'].value
if severity == 'error':
result_errors.append(result)
elif severity == 'warning':
result_warnings.append(result)
return (result_errors, result_warnings)
def _GetModifiedFiles():
root = git.repository_root()
sha = git.last_commit()
working_files = git.modified_files(root)
if working_files:
print 'You need to commit all the files before running Checkstyle'
sys.exit(1)
modified_files = git.modified_files(root, True, sha)
modified_files = {os.path.abspath(f): modified_files[f] for f
in modified_files if f.endswith('.java')}
return (sha, modified_files)
def main(args=None):
parser = argparse.ArgumentParser()
parser.add_argument('--file', '-f', nargs='+')
args = parser.parse_args()
RunCheckstyle(args.file)
if __name__ == '__main__':
main()