#!/usr/bin/env python # Check that the input file has no external include guards. # Returns with 0 exit code on success, 1 otherwise. import re import sys import subprocess def git(*args, **kwargs): return subprocess.check_output(['git'] + list(args), **kwargs) def get_changed_paths(filter): output = git('diff', '--cached', '--name-only', '-z', '--diff-filter='+filter) return output.split('\0')[:-1] # remove trailing '' def get_against(): try: head = git('rev-parse', '--verify', 'HEAD', stderr=None) except subprocess.CalledProcessError: # Initial commit: diff against an empty tree object return '4b825dc642cb6eb9a060e54bf8d69288fbee4904' else: return 'HEAD' against = get_against() success = True def croak(path, line, msg, *args): global success success = False if path is not None: sys.stderr.write("%s:%d: " % (path, line or 0)) sys.stderr.write(msg % args if args else msg) if msg[-1] != '\n': sys.stderr.write('\n') def check_filenames(): try: allownonascii = git('config', '--get', '--bool', 'hooks.allownonascii') except subprocess.CalledProcessError: pass else: if allownonascii == 'true': return for path in get_changed_paths('ACR'): try: path.decode('ascii') except UnicodeDecodeError: croak(path, 0, "Non-ASCII file name") def check_whitespace(): try: git('diff-index', '--check', '--cached', against, stderr=None) except subprocess.CalledProcessError as e: sys.stderr.write(e.output) global success success = False guard_re = re.compile('^[ \t]*#\s*ifndef\s+_.*?_H(PP)?\n' '\s*#\s*include\s+(".*?")\s*\n' '\s*#\s*endif.*?$', re.MULTILINE) def check_external_guards (infile): contents = infile.read() for m in guard_re.finditer(contents): lineno = 1 + contents[:m.start()].count('\n') croak(infile.name, lineno, "External include guard") croak(None, None, m.group(0)) def check_changed_files(): for path in get_changed_paths('AM'): check_external_guards(open(path)) def main(): check_filenames() check_changed_files() check_whitespace() if not success: exit(1) if __name__ == '__main__': main()