diff --git a/Makefile.am b/Makefile.am index 5865eb098e920682ed632c804faefc3cbadcbaa9..f9ebfeb8dbdb7d134abddea8ff06ce75f7da093b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,7 @@ ACLOCAL_AMFLAGS = -I autotools BUILD_BASH_COMPLETION = $(top_srcdir)/autotools/build-bash-completion RUN_IN_TEMPDIR = $(top_srcdir)/autotools/run-in-tempdir CHECK_PYTHON_CODE = $(top_srcdir)/autotools/check-python-code +CHECK_HEADER = $(top_srcdir)/autotools/check-header CHECK_MAN = $(top_srcdir)/autotools/check-man CHECK_VERSION = $(top_srcdir)/autotools/check-version CHECK_NEWS = $(top_srcdir)/autotools/check-news @@ -560,6 +561,7 @@ EXTRA_DIST = \ pylintrc \ autotools/build-bash-completion \ autotools/build-rpc \ + autotools/check-header \ autotools/check-python-code \ autotools/check-imports \ autotools/check-man \ @@ -804,6 +806,7 @@ srclink_files = \ check_python_code = \ $(BUILD_BASH_COMPLETION) \ $(CHECK_IMPORTS) \ + $(CHECK_HEADER) \ $(DOCPP) \ $(all_python_code) @@ -815,6 +818,7 @@ lint_python_code = \ $(pkglib_python_scripts) \ $(BUILD_BASH_COMPLETION) \ $(CHECK_IMPORTS) \ + $(CHECK_HEADER) \ $(DOCPP) \ $(PYTHON_BOOTSTRAP) @@ -829,6 +833,7 @@ pep8_python_code = \ $(dist_tools_PYTHON) \ $(pkglib_python_scripts) \ $(BUILD_BASH_COMPLETION) \ + $(CHECK_HEADER) \ $(DOCPP) \ $(PYTHON_BOOTSTRAP) \ qa @@ -1124,6 +1129,7 @@ check-dirs: $(BUILT_SOURCES) .PHONY: check-local check-local: check-dirs $(BUILT_SOURCES) $(CHECK_PYTHON_CODE) $(check_python_code) + PYTHONPATH=. $(CHECK_HEADER) $(check_python_code) $(CHECK_VERSION) $(VERSION) $(top_srcdir)/NEWS $(CHECK_NEWS) < $(top_srcdir)/NEWS PYTHONPATH=. $(RUN_IN_TEMPDIR) $(CURDIR)/$(CHECK_IMPORTS) . $(standalone_python_modules) diff --git a/autotools/check-header b/autotools/check-header new file mode 100755 index 0000000000000000000000000000000000000000..8ebaef2816134f4d032269af8d5c2e645d934c1d --- /dev/null +++ b/autotools/check-header @@ -0,0 +1,139 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script to verify file header. + +""" + +# pylint: disable-msg=C0103 +# [C0103] Invalid name + +import sys +import re +import itertools + +from ganeti import constants +from ganeti import utils +from ganeti import compat + + +#: Assume header is always in the first 8kB of a file +_READ_SIZE = 8 * 1024 + +_GPLv2 = [ + "This program is free software; you can redistribute it and/or modify", + "it under the terms of the GNU General Public License as published by", + "the Free Software Foundation; either version 2 of the License, or", + "(at your option) any later version.", + "", + "This program is distributed in the hope that it will be useful, but", + "WITHOUT ANY WARRANTY; without even the implied warranty of", + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", + "General Public License for more details.", + "", + "You should have received a copy of the GNU General Public License", + "along with this program; if not, write to the Free Software", + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA", + "02110-1301, USA.", + ] + + +_SHEBANG = re.compile(r"^#(?:|!(?:/usr/bin/python(?:| -u)|/bin/(?:|ba)sh))$") +_COPYRIGHT_YEAR = r"20[01][0-9]" +_COPYRIGHT = re.compile(r"# Copyright \(C\) (%s(?:, %s)*) Google Inc\.$" % + (_COPYRIGHT_YEAR, _COPYRIGHT_YEAR)) +_COPYRIGHT_DESC = "Copyright (C) <year>[, <year> ...] Google Inc." +_AUTOGEN = "# This file is automatically generated, do not edit!" + + +class HeaderError(Exception): + pass + + +def _Fail(lineno, msg): + raise HeaderError("Line %s: %s" % (lineno, msg)) + + +def _CheckHeader(getline_fn): + (lineno, line) = getline_fn() + + if line == _AUTOGEN: + return + + if not _SHEBANG.match(line): + _Fail(lineno, ("Must contain nothing but a hash character (#) or a" + " shebang line (e.g. #!/bin/bash)")) + + (lineno, line) = getline_fn() + + if line == _AUTOGEN: + return + + if line != "#": + _Fail(lineno, "Must contain nothing but hash character (#)") + + (lineno, line) = getline_fn() + if line: + _Fail(lineno, "Must be empty") + + (lineno, line) = getline_fn() + if not _COPYRIGHT.match(line): + _Fail(lineno, "Must contain copyright information (%s)" % _COPYRIGHT_DESC) + + (lineno, line) = getline_fn() + if line != "#": + _Fail(lineno, "Must contain nothing but hash character (#)") + + for licence_line in _GPLv2: + (lineno, line) = getline_fn() + if line != ("# %s" % licence_line).rstrip(): + _Fail(lineno, "Does not match expected licence line (%s)" % licence_line) + + (lineno, line) = getline_fn() + if line: + _Fail(lineno, "Must be empty") + + +def Main(): + """Main program. + + """ + fail = False + + for filename in sys.argv[1:]: + content = utils.ReadFile(filename, size=_READ_SIZE) + lines = zip(itertools.count(1), content.splitlines()) + + try: + _CheckHeader(compat.partial(lines.pop, 0)) + except HeaderError, err: + report = str(err) + print "%s: %s" % (filename, report) + fail = True + + if fail: + sys.exit(constants.EXIT_FAILURE) + else: + sys.exit(constants.EXIT_SUCCESS) + + +if __name__ == "__main__": + Main()