1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#-------------------------------------------------------------------------
5# drawElements Quality Program utilities
6# --------------------------------------
7#
8# Copyright 2015 The Android Open Source Project
9#
10# Licensed under the Apache License, Version 2.0 (the "License");
11# you may not use this file except in compliance with the License.
12# You may obtain a copy of the License at
13#
14#      http://www.apache.org/licenses/LICENSE-2.0
15#
16# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS,
18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19# See the License for the specific language governing permissions and
20# limitations under the License.
21#
22#-------------------------------------------------------------------------
23
24# Check that the input file has no external include guards.
25# Returns with 0 exit code on success, 1 otherwise.
26
27import re
28import sys
29import subprocess
30
31def git(*args, **kwargs):
32    return subprocess.check_output(['git'] + list(args), **kwargs)
33
34def get_changed_paths(filter):
35    output = git('diff', '--cached', '--name-only', '-z', '--diff-filter='+filter)
36    return output.split('\0')[:-1] # remove trailing ''
37
38def get_against():
39    try:
40        head = git('rev-parse', '--verify', 'HEAD', stderr=None)
41    except subprocess.CalledProcessError:
42        # Initial commit: diff against an empty tree object
43        return '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
44    else:
45        return 'HEAD'
46
47against = get_against()
48
49success = True
50
51def croak(path, line, msg, *args):
52    global success
53    success = False
54    if path is not None:
55        sys.stderr.write("%s:%d: " % (path, line or 0))
56    sys.stderr.write(msg % args if args else msg)
57    if msg[-1] != '\n':
58        sys.stderr.write('\n')
59
60def check_filenames():
61    try:
62        allownonascii = git('config', '--get', '--bool', 'hooks.allownonascii')
63    except subprocess.CalledProcessError:
64        pass
65    else:
66        if allownonascii == 'true':
67            return
68
69    for path in get_changed_paths('ACR'):
70        try:
71            path.decode('ascii')
72        except UnicodeDecodeError:
73            croak(path, 0, "Non-ASCII file name")
74
75def check_whitespace():
76    try:
77        git('diff-index', '--check', '--cached', against, stderr=None)
78    except subprocess.CalledProcessError as e:
79        sys.stderr.write(e.output)
80        global success
81        success = False
82
83guard_re = re.compile('^[ \t]*#\s*ifndef\s+_.*?_H(PP)?\n'
84                      '\s*#\s*include\s+(".*?")\s*\n'
85                      '\s*#\s*endif.*?$',
86                      re.MULTILINE)
87
88def check_external_guards (infile):
89    contents = infile.read()
90    for m in guard_re.finditer(contents):
91        lineno = 1 + contents[:m.start()].count('\n')
92        croak(infile.name, lineno, "External include guard")
93        croak(None, None, m.group(0))
94
95def check_changed_files():
96    for path in get_changed_paths('AM'):
97        check_external_guards(open(path))
98
99def main():
100    check_filenames()
101    check_changed_files()
102    check_whitespace()
103    if not success:
104        exit(1)
105
106if __name__ == '__main__':
107    main()
108