1b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas#!/usr/bin/python 2b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 3b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# 4b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# Copyright 2015, The Android Open Source Project 5b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# 6b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# Licensed under the Apache License, Version 2.0 (the "License"); 7b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# you may not use this file except in compliance with the License. 8b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# You may obtain a copy of the License at 9b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# 10b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# http://www.apache.org/licenses/LICENSE-2.0 11b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# 12b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# Unless required by applicable law or agreed to in writing, software 13b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# distributed under the License is distributed on an "AS IS" BASIS, 14b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# See the License for the specific language governing permissions and 16b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# limitations under the License. 17b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas# 18b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 19b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas"""Script that is used by developers to run style checks on Java files.""" 20b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 21b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport argparse 22b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport errno 23b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport os 24b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport shutil 25b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport subprocess 26b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport sys 27b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport tempfile 28b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport xml.dom.minidom 29b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasimport gitlint.git as git 30b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 31b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 32b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _FindFoldersContaining(root, wanted): 33b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Recursively finds directories that have a file with the given name. 34b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 35b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 36b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas root: Root folder to start the search from. 37b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas wanted: The filename that we are looking for. 38b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 39b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 40b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas List of folders that has a file with the given name 41b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 42b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 43b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not root: 44b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return [] 45b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if os.path.islink(root): 46b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return [] 47b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result = [] 48b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for file_name in os.listdir(root): 49b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_path = os.path.join(root, file_name) 50b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if os.path.isdir(file_path): 51b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sub_result = _FindFoldersContaining(file_path, wanted) 52b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result.extend(sub_result) 53b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas else: 54b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if file_name == wanted: 55b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result.append(root) 56b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return result 57b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 58b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasMAIN_DIRECTORY = os.path.normpath(os.path.dirname(__file__)) 59b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasCHECKSTYLE_JAR = os.path.join(MAIN_DIRECTORY, 'checkstyle.jar') 60b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasCHECKSTYLE_STYLE = os.path.join(MAIN_DIRECTORY, 'android-style.xml') 61b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasFORCED_RULES = ['com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck', 62b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 'com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck'] 63b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasSKIPPED_RULES_FOR_TEST_FILES = ['com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck', 64b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 'com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck'] 65b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasSUBPATH_FOR_TEST_FILES = ['/tests/', '/test/', '/androidTest/'] 66b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasSUBPATH_FOR_TEST_DATA_FILES = _FindFoldersContaining(git.repository_root(), 67b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 'IGNORE_CHECKSTYLE') 68b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasERROR_UNCOMMITTED = 'You need to commit all modified files before running Checkstyle\n' 69b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas LiutikasERROR_UNTRACKED = 'You have untracked java files that are not being checked:\n' 70b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 71b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 72b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef RunCheckstyleOnFiles(java_files, classpath=CHECKSTYLE_JAR, config_xml=CHECKSTYLE_STYLE): 73b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Runs Checkstyle checks on a given set of java_files. 74b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 75b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 76b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas java_files: A list of files to check. 77b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath: The colon-delimited list of JARs in the classpath. 78b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml: Path of the checkstyle XML configuration file. 79b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 80b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 81b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas A tuple of errors and warnings. 82b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 83b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'Running Checkstyle on inputted files' 84b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas java_files = map(os.path.abspath, java_files) 85b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas stdout = _ExecuteCheckstyle(java_files, classpath, config_xml) 86b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas (errors, warnings) = _ParseAndFilterOutput(stdout) 87b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas _PrintErrorsAndWarnings(errors, warnings) 88b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return errors, warnings 89b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 90b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 91b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef RunCheckstyleOnACommit(commit, 92b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath=CHECKSTYLE_JAR, 93b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml=CHECKSTYLE_STYLE, 94b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_whitelist=None): 95b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Runs Checkstyle checks on a given commit. 96b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 97b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas It will run Checkstyle on the changed Java files in a specified commit SHA-1 98b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas and if that is None it will fallback to check the latest commit of the 99b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas currently checked out branch. 100b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 101b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 102b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit: A full 40 character SHA-1 of a commit to check. 103b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath: The colon-delimited list of JARs in the classpath. 104b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml: Path of the checkstyle XML configuration file. 105b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_whitelist: A list of whitelisted file paths that should be checked. 106b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 107b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 108b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas A tuple of errors and warnings. 109b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 110b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not git.repository_root(): 111b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'FAILURE: not inside a git repository' 112b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(1) 113b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas explicit_commit = commit is not None 114b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not explicit_commit: 115b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas _WarnIfUntrackedFiles() 116b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit = git.last_commit() 117b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'Running Checkstyle on %s commit' % commit 118b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files = _GetModifiedFiles(commit, explicit_commit) 119b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files = _FilterFiles(commit_modified_files, file_whitelist) 120b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not commit_modified_files.keys(): 121b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'No Java files to check' 122b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return [], [] 123b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 124b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas (tmp_dir, tmp_file_map) = _GetTempFilesForCommit( 125b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files.keys(), commit) 126b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 127b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas java_files = tmp_file_map.keys() 128b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas stdout = _ExecuteCheckstyle(java_files, classpath, config_xml) 129b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 130b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # Remove all the temporary files. 131b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas shutil.rmtree(tmp_dir) 132b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 133b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas (errors, warnings) = _ParseAndFilterOutput(stdout, 134b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit, 135b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files, 136b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file_map) 137b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas _PrintErrorsAndWarnings(errors, warnings) 138b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return errors, warnings 139b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 140b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 141b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _WarnIfUntrackedFiles(out=sys.stdout): 142b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Prints a warning and a list of untracked files if needed.""" 143b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas root = git.repository_root() 144b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas untracked_files = git.modified_files(root, False) 145b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas untracked_files = {f for f in untracked_files if f.endswith('.java')} 146b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if untracked_files: 147b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas out.write(ERROR_UNTRACKED) 148b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for untracked_file in untracked_files: 149b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas out.write(untracked_file + '\n') 150b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas out.write('\n') 151b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 152b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 153b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _PrintErrorsAndWarnings(errors, warnings): 154b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Prints given errors and warnings.""" 155b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if errors: 156b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'ERRORS:' 157b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print '\n'.join(errors) 158b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if warnings: 159b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'WARNINGS:' 160b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print '\n'.join(warnings) 161b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 162b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 163b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _ExecuteCheckstyle(java_files, classpath, config_xml): 164b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Runs Checkstyle to check give Java files for style errors. 165b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 166b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 167b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas java_files: A list of Java files that needs to be checked. 168b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath: The colon-delimited list of JARs in the classpath. 169b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml: Path of the checkstyle XML configuration file. 170b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 171b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 172b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Checkstyle output in XML format. 173b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 174b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # Run checkstyle 175b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas checkstyle_env = os.environ.copy() 176b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas checkstyle_env['JAVA_CMD'] = 'java' 177b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas try: 178b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas check = subprocess.Popen(['java', '-cp', classpath, 179b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 'com.puppycrawl.tools.checkstyle.Main', '-c', 180b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml, '-f', 'xml'] + java_files, 181b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas stdout=subprocess.PIPE, env=checkstyle_env) 182b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas stdout, _ = check.communicate() 183b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas except OSError as e: 184b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if e.errno == errno.ENOENT: 185b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'Error running Checkstyle!' 186b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(1) 187b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 188b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # A work-around for Checkstyle printing error count to stdio. 189b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if 'Checkstyle ends with' in stdout.splitlines()[-1]: 190b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas stdout = '\n'.join(stdout.splitlines()[:-1]) 191b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return stdout 192b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 193b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 194b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _ParseAndFilterOutput(stdout, 195b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sha=None, 196b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files=None, 197b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file_map=None): 198b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result_errors = [] 199b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result_warnings = [] 200b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas root = xml.dom.minidom.parseString(stdout) 201b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for file_element in root.getElementsByTagName('file'): 202b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_name = file_element.attributes['name'].value 203b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if tmp_file_map: 204b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_name = tmp_file_map[file_name] 205b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas modified_lines = None 206b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if commit_modified_files: 207b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas modified_lines = git.modified_lines(file_name, 208b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_modified_files[file_name], 209b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sha) 210b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_class = any(substring in file_name for substring 211b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas in SUBPATH_FOR_TEST_FILES) 212b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_data_class = any(substring in file_name for substring 213b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas in SUBPATH_FOR_TEST_DATA_FILES) 214b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_name = os.path.relpath(file_name) 215b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas errors = file_element.getElementsByTagName('error') 216b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for error in errors: 217b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas line = int(error.attributes['line'].value) 218b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas rule = error.attributes['source'].value 219b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if _ShouldSkip(commit_modified_files, modified_lines, line, rule, 220b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_class, test_data_class): 221b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas continue 222b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 223b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas column = '' 224b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if error.hasAttribute('column'): 225b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas column = '%s:' % error.attributes['column'].value 226b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas message = error.attributes['message'].value 227b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas project = '' 228b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if os.environ.get('REPO_PROJECT'): 229b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas project = '[' + os.environ.get('REPO_PROJECT') + '] ' 230b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 231b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result = ' %s%s:%s:%s %s' % (project, file_name, line, column, message) 232b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 233b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas severity = error.attributes['severity'].value 234b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if severity == 'error': 235b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result_errors.append(result) 236b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas elif severity == 'warning': 237b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas result_warnings.append(result) 238b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return result_errors, result_warnings 239b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 240b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 241b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _ShouldSkip(commit_check, modified_lines, line, rule, test_class=False, 242b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_data_class=False): 243b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Returns whether an error on a given line should be skipped. 244b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 245b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 246b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit_check: Whether Checkstyle is being run on a specific commit. 247b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas modified_lines: A list of lines that has been modified. 248b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas line: The line that has a rule violation. 249b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas rule: The type of rule that a given line is violating. 250b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_class: Whether the file being checked is a test class. 251b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas test_data_class: Whether the file being check is a class used as test data. 252b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 253b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 254b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas A boolean whether a given line should be skipped in the reporting. 255b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 256b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # None modified_lines means checked file is new and nothing should be skipped. 257b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if test_data_class: 258b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return True 259b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if test_class and rule in SKIPPED_RULES_FOR_TEST_FILES: 260b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return True 261b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not commit_check: 262b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return False 263b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if modified_lines is None: 264b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return False 265b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return line not in modified_lines and rule not in FORCED_RULES 266b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 267b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 268b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _GetModifiedFiles(commit, explicit_commit=False, out=sys.stdout): 269b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas root = git.repository_root() 270b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas pending_files = git.modified_files(root, True) 271b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if pending_files and not explicit_commit: 272b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas out.write(ERROR_UNCOMMITTED) 273b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(1) 274b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 275b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas modified_files = git.modified_files(root, True, commit) 276b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas modified_files = {f: modified_files[f] for f 277b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas in modified_files if f.endswith('.java')} 278b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return modified_files 279b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 280b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 281b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _FilterFiles(files, file_whitelist): 282b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not file_whitelist: 283b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return files 284b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return {f: files[f] for f in files 285b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for whitelist in file_whitelist if whitelist in f} 286b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 287b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 288b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef _GetTempFilesForCommit(file_names, commit): 289b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Creates a temporary snapshot of the files in at a commit. 290b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 291b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Retrieves the state of every file in file_names at a given commit and writes 292b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas them all out to a temporary directory. 293b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 294b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Args: 295b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas file_names: A list of files that need to be retrieved. 296b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas commit: A full 40 character SHA-1 of a commit. 297b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 298b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas Returns: 299b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas A tuple of temprorary directory name and a directionary of 300b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas temp_file_name: filename. For example: 301b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 302b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas ('/tmp/random/', {'/tmp/random/blarg.java': 'real/path/to/file.java' } 303b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 304b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_dir_name = tempfile.mkdtemp() 305b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file_names = {} 306b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas for file_name in file_names: 307b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas rel_path = os.path.relpath(file_name) 308b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas content = subprocess.check_output( 309b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas ['git', 'show', commit + ':' + rel_path]) 310b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 311b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file_name = os.path.join(tmp_dir_name, rel_path) 312b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # create directory for the file if it doesn't exist 313b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not os.path.exists(os.path.dirname(tmp_file_name)): 314b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas os.makedirs(os.path.dirname(tmp_file_name)) 315b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 316b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file = open(tmp_file_name, 'w') 317b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file.write(content) 318b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file.close() 319b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas tmp_file_names[tmp_file_name] = file_name 320b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas return tmp_dir_name, tmp_file_names 321b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 322b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 323b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasdef main(args=None): 324b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """Runs Checkstyle checks on a given set of java files or a commit. 325b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 326b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas It will run Checkstyle on the list of java files first, if unspecified, 327b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas then the check will be run on a specified commit SHA-1 and if that 328b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas is None it will fallback to check the latest commit of the currently checked 329b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas out branch. 330b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas """ 331b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser = argparse.ArgumentParser() 332b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser.add_argument('--file', '-f', nargs='+') 333b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser.add_argument('--sha', '-s') 334b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser.add_argument('--config_xml', '-c') 335b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser.add_argument('--file_whitelist', '-fw', nargs='+') 336b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas parser.add_argument('--add_classpath', '-p') 337b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas args = parser.parse_args() 338b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 339b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas config_xml = args.config_xml or CHECKSTYLE_STYLE 340b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 341b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if not os.path.exists(config_xml): 342b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'Java checkstyle configuration file is missing' 343b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(1) 344b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 345b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath = CHECKSTYLE_JAR 346b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 347b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if args.add_classpath: 348b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas classpath = args.add_classpath + ':' + classpath 349b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 350b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if args.file: 351b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas # Files to check were specified via command line. 352b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas (errors, warnings) = RunCheckstyleOnFiles(args.file, classpath, config_xml) 353b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas else: 354b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas (errors, warnings) = RunCheckstyleOnACommit(args.sha, classpath, config_xml, 355b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas args.file_whitelist) 356b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 357b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas if errors or warnings: 358b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(1) 359b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 360b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas print 'SUCCESS! NO ISSUES FOUND' 361b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas sys.exit(0) 362b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 363b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas 364b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikasif __name__ == '__main__': 365b782f222d5ad3c1250acb12260f2ddaa0935ca4Aurimas Liutikas main() 366