Add tool to check Python file headers
[ganeti-local] / autotools / check-header
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script to verify file header.
23
24 """
25
26 # pylint: disable-msg=C0103
27 # [C0103] Invalid name
28
29 import sys
30 import re
31 import itertools
32
33 from ganeti import constants
34 from ganeti import utils
35 from ganeti import compat
36
37
38 #: Assume header is always in the first 8kB of a file
39 _READ_SIZE = 8 * 1024
40
41 _GPLv2 = [
42   "This program is free software; you can redistribute it and/or modify",
43   "it under the terms of the GNU General Public License as published by",
44   "the Free Software Foundation; either version 2 of the License, or",
45   "(at your option) any later version.",
46   "",
47   "This program is distributed in the hope that it will be useful, but",
48   "WITHOUT ANY WARRANTY; without even the implied warranty of",
49   "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU",
50   "General Public License for more details.",
51   "",
52   "You should have received a copy of the GNU General Public License",
53   "along with this program; if not, write to the Free Software",
54   "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA",
55   "02110-1301, USA.",
56   ]
57
58
59 _SHEBANG = re.compile(r"^#(?:|!(?:/usr/bin/python(?:| -u)|/bin/(?:|ba)sh))$")
60 _COPYRIGHT_YEAR = r"20[01][0-9]"
61 _COPYRIGHT = re.compile(r"# Copyright \(C\) (%s(?:, %s)*) Google Inc\.$" %
62                         (_COPYRIGHT_YEAR, _COPYRIGHT_YEAR))
63 _COPYRIGHT_DESC = "Copyright (C) <year>[, <year> ...] Google Inc."
64 _AUTOGEN = "# This file is automatically generated, do not edit!"
65
66
67 class HeaderError(Exception):
68   pass
69
70
71 def _Fail(lineno, msg):
72   raise HeaderError("Line %s: %s" % (lineno, msg))
73
74
75 def _CheckHeader(getline_fn):
76   (lineno, line) = getline_fn()
77
78   if line == _AUTOGEN:
79     return
80
81   if not _SHEBANG.match(line):
82     _Fail(lineno, ("Must contain nothing but a hash character (#) or a"
83                    " shebang line (e.g. #!/bin/bash)"))
84
85   (lineno, line) = getline_fn()
86
87   if line == _AUTOGEN:
88     return
89
90   if line != "#":
91     _Fail(lineno, "Must contain nothing but hash character (#)")
92
93   (lineno, line) = getline_fn()
94   if line:
95     _Fail(lineno, "Must be empty")
96
97   (lineno, line) = getline_fn()
98   if not _COPYRIGHT.match(line):
99     _Fail(lineno, "Must contain copyright information (%s)" % _COPYRIGHT_DESC)
100
101   (lineno, line) = getline_fn()
102   if line != "#":
103     _Fail(lineno, "Must contain nothing but hash character (#)")
104
105   for licence_line in _GPLv2:
106     (lineno, line) = getline_fn()
107     if line != ("# %s" % licence_line).rstrip():
108       _Fail(lineno, "Does not match expected licence line (%s)" % licence_line)
109
110   (lineno, line) = getline_fn()
111   if line:
112     _Fail(lineno, "Must be empty")
113
114
115 def Main():
116   """Main program.
117
118   """
119   fail = False
120
121   for filename in sys.argv[1:]:
122     content = utils.ReadFile(filename, size=_READ_SIZE)
123     lines = zip(itertools.count(1), content.splitlines())
124
125     try:
126       _CheckHeader(compat.partial(lines.pop, 0))
127     except HeaderError, err:
128       report = str(err)
129       print "%s: %s" % (filename, report)
130       fail = True
131
132   if fail:
133     sys.exit(constants.EXIT_FAILURE)
134   else:
135     sys.exit(constants.EXIT_SUCCESS)
136
137
138 if __name__ == "__main__":
139   Main()