Created
February 7, 2018 07:16
-
-
Save goldsborough/a004efc8717911f06c000b83e6c75989 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import json | |
import re | |
import subprocess | |
import sys | |
DEFAULT_FILE_PATTERN = r'.*\.[ch](pp)?' | |
# @@ -start,count +start,count @@ | |
CHUNK_PATTERN = r'^@@\s+-\d+,\d+\s+\+(\d+),(\d+)\s+@@' | |
def transform_globs_into_regexes(globs): | |
return [glob.replace('*', '.*').replace('?', '.') for glob in globs] | |
def get_file_patterns(globs, regexes): | |
regexes += transform_globs_into_regexes(globs) | |
if not regexes: | |
regexes = [DEFAULT_FILE_PATTERN] | |
return [re.compile(regex) for regex in regexes] | |
def git_diff(args): | |
command = ['git', '--no-pager', 'diff', '--no-color'] + args | |
try: | |
return subprocess.check_output(command, stderr=subprocess.STDOUT) | |
except OSError: | |
_, e, _ = sys.exc_info() | |
raise RuntimeError('Error executing git diff: {}'.format(e)) | |
def get_changed_files(revision, paths, file_patterns): | |
args = [ | |
'--diff-filter', | |
'AMU', | |
'--ignore-all-space', | |
'--name-only', | |
revision, | |
] | |
output = git_diff(args + paths) | |
files = [] | |
for line in output.split('\n'): | |
for pattern in file_patterns: | |
if pattern.match(line): | |
files.append(line) | |
return files | |
def get_changed_lines(revision, filename): | |
output = git_diff(['--unified=0', revision, filename]) | |
changed_lines = [] | |
for chunk in re.finditer(CHUNK_PATTERN, output, re.MULTILINE): | |
start = int(chunk.group(1)) | |
count = int(chunk.group(2)) | |
changed_lines.append([start, start + count]) | |
return dict(name=filename, lines=changed_lines) | |
def run_clang_tidy(clang_tidy_path, compile_commands_directory, extra_args, | |
line_filters): | |
command = ['clang-tidy', '-p', compile_commands_directory] | |
if line_filters: | |
command.append("--line-filter='{}'".format(json.dumps(line_filters))) | |
try: | |
output = subprocess.check_output(command + extra_args) | |
except OSError: | |
_, e, _ = sys.exc_info() | |
raise RuntimeError('Error executing clang-tidy: {}'.format(e)) | |
else: | |
return output | |
def parse_options(): | |
parser = argparse.ArgumentParser( | |
description='Run Clang-Tidy on your Git changes') | |
parser.add_argument('-a', '--all', action='store_true') | |
parser.add_argument('-c', '--clang-tidy-path', default='clang-tidy') | |
parser.add_argument('--extra-args', nargs='+', default=[]) | |
parser.add_argument('-g', '--glob', nargs='+', default=[]) | |
parser.add_argument('-x', '--regex', nargs='+', default=[]) | |
parser.add_argument('-d', '--compile-commands-dir', default='.') | |
parser.add_argument('-r', '--revision', default='HEAD') | |
parser.add_argument('-p', '--paths', nargs='+', default=['.']) | |
return parser.parse_args() | |
def main(): | |
options = parse_options() | |
file_patterns = get_file_patterns(options.glob, options.regex) | |
files = get_changed_files(options.revision, options.paths, file_patterns) | |
line_filters = [] | |
if not options.all: | |
for filename in files: | |
line_filters.append(get_changed_lines(options.revision, filename)) | |
result = run_clang_tidy(options.clang_tidy_path, | |
options.compile_commands_dir, options.extra_args, | |
line_filters) | |
print(result) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment