Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 
      3 # Check that the input file has no external include guards.
      4 # Returns with 0 exit code on success, 1 otherwise.
      5 
      6 import re
      7 import sys
      8 import subprocess
      9 
     10 def git(*args, **kwargs):
     11     return subprocess.check_output(['git'] + list(args), **kwargs)
     12 
     13 def get_changed_paths(filter):
     14     output = git('diff', '--cached', '--name-only', '-z', '--diff-filter='+filter)
     15     return output.split('\0')[:-1] # remove trailing ''
     16 
     17 def get_against():
     18     try:
     19         head = git('rev-parse', '--verify', 'HEAD', stderr=None)
     20     except subprocess.CalledProcessError:
     21         # Initial commit: diff against an empty tree object
     22         return '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
     23     else:
     24         return 'HEAD'
     25 
     26 against = get_against()
     27 
     28 success = True
     29 
     30 def croak(path, line, msg, *args):
     31     global success
     32     success = False
     33     if path is not None:
     34         sys.stderr.write("%s:%d: " % (path, line or 0))
     35     sys.stderr.write(msg % args if args else msg)
     36     if msg[-1] != '\n':
     37         sys.stderr.write('\n')
     38 
     39 def check_filenames():
     40     try:
     41         allownonascii = git('config', '--get', '--bool', 'hooks.allownonascii')
     42     except subprocess.CalledProcessError:
     43         pass
     44     else:
     45         if allownonascii == 'true':
     46             return
     47 
     48     for path in get_changed_paths('ACR'):
     49         try:
     50             path.decode('ascii')
     51         except UnicodeDecodeError:
     52             croak(path, 0, "Non-ASCII file name")
     53 
     54 def check_whitespace():
     55     try:
     56         git('diff-index', '--check', '--cached', against, stderr=None)
     57     except subprocess.CalledProcessError as e:
     58         sys.stderr.write(e.output)
     59         global success
     60         success = False
     61 
     62 guard_re = re.compile('^[ \t]*#\s*ifndef\s+_.*?_H(PP)?\n'
     63                       '\s*#\s*include\s+(".*?")\s*\n'
     64                       '\s*#\s*endif.*?$',
     65                       re.MULTILINE)
     66 
     67 def check_external_guards (infile):
     68     contents = infile.read()
     69     for m in guard_re.finditer(contents):
     70         lineno = 1 + contents[:m.start()].count('\n')
     71         croak(infile.name, lineno, "External include guard")
     72         croak(None, None, m.group(0))
     73 
     74 def check_changed_files():
     75     for path in get_changed_paths('AM'):
     76         check_external_guards(open(path))
     77 
     78 def main():
     79     check_filenames()
     80     check_changed_files()
     81     check_whitespace()
     82     if not success:
     83         exit(1)
     84 
     85 if __name__ == '__main__':
     86     main()
     87