diff --git a/Makefile.am b/Makefile.am index 9fe24fb65b..8153496ded 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,7 +47,7 @@ EXTRA_DIST = \ AUTHORS.in \ scripts/augeas-gentest.py \ build-aux/check-spacing.pl \ - build-aux/header-ifdef.pl \ + scripts/header-ifdef.py \ scripts/minimize-po.py \ scripts/mock-noinline.py \ scripts/prohibit-duplicate-header.py \ diff --git a/build-aux/header-ifdef.pl b/build-aux/header-ifdef.pl deleted file mode 100644 index dba3dbcbdc..0000000000 --- a/build-aux/header-ifdef.pl +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/perl -# -# Validate that header files follow a standard layout: -# -# /* -# ...copyright header... -# */ -# -# #pragma once -# ....content.... -# -#--- -# -# For any file ending priv.h, before the #pragma once -# We will have a further section -# -# #ifndef SYMBOL_ALLOW -# # error .... -# #endif /* SYMBOL_ALLOW */ -# -# -#--- -# -# For public headers (files in include/), use the standard header guard instead of #pragma once: -# #ifndef SYMBOL -# # define SYMBOL -# ....content.... -# #endif /* SYMBOL */ - -use strict; -use warnings; - -my $STATE_COPYRIGHT_COMMENT = 0; -my $STATE_COPYRIGHT_BLANK = 1; -my $STATE_PRIV_START = 2; -my $STATE_PRIV_ERROR = 3; -my $STATE_PRIV_END = 4; -my $STATE_PRIV_BLANK = 5; -my $STATE_GUARD_START = 6; -my $STATE_GUARD_DEFINE = 7; -my $STATE_GUARD_END = 8; -my $STATE_EOF = 9; -my $STATE_PRAGMA = 10; - -my $file = " "; -my $ret = 0; -my $ifdef = ""; -my $ifdefpriv = ""; -my $publicheader = 0; - -my $state = $STATE_EOF; -my $mistake = 0; - -sub mistake { - my $msg = shift; - warn $msg; - $mistake = 1; - $ret = 1; -} - -while (<>) { - if (not $file eq $ARGV) { - if ($state == $STATE_COPYRIGHT_COMMENT) { - &mistake("$file: missing copyright comment"); - } elsif ($state == $STATE_COPYRIGHT_BLANK) { - &mistake("$file: missing blank line after copyright header"); - } elsif ($state == $STATE_PRIV_START) { - &mistake("$file: missing '#ifndef $ifdefpriv'"); - } elsif ($state == $STATE_PRIV_ERROR) { - &mistake("$file: missing '# error ...priv allow...'"); - } elsif ($state == $STATE_PRIV_END) { - &mistake("$file: missing '#endif /* $ifdefpriv */'"); - } elsif ($state == $STATE_PRIV_BLANK) { - &mistake("$file: missing blank line after priv header check"); - } elsif ($state == $STATE_GUARD_START) { - if ($publicheader) { - &mistake("$file: missing '#ifndef $ifdef'"); - } else { - &mistake("$file: missing '#pragma once' header guard"); - } - } elsif ($state == $STATE_GUARD_DEFINE) { - &mistake("$file: missing '# define $ifdef'"); - } elsif ($state == $STATE_GUARD_END) { - &mistake("$file: missing '#endif /* $ifdef */'"); - } - - $ifdef = uc $ARGV; - $ifdef =~ s,.*/,,; - $ifdef =~ s,[^A-Z0-9],_,g; - $ifdef =~ s,__+,_,g; - unless ($ifdef =~ /^LIBVIRT_/ && $ARGV !~ /libvirt_internal.h/) { - $ifdef = "LIBVIRT_" . $ifdef; - } - $ifdefpriv = $ifdef . "_ALLOW"; - - $file = $ARGV; - $state = $STATE_COPYRIGHT_COMMENT; - $mistake = 0; - $publicheader = ($ARGV =~ /include\//); - } - - if ($mistake || - $ARGV =~ /config-post\.h$/ || - $ARGV =~ /vbox_(CAPI|XPCOM)/) { - $state = $STATE_EOF; - next; - } - - if ($state == $STATE_COPYRIGHT_COMMENT) { - if (m,\*/,) { - $state = $STATE_COPYRIGHT_BLANK; - } - } elsif ($state == $STATE_COPYRIGHT_BLANK) { - if (! /^$/) { - &mistake("$file: missing blank line after copyright header"); - } - if ($ARGV =~ /priv\.h$/) { - $state = $STATE_PRIV_START; - } else { - $state = $STATE_GUARD_START; - } - } elsif ($state == $STATE_PRIV_START) { - if (/^$/) { - &mistake("$file: too many blank lines after copyright header"); - } elsif (/#ifndef $ifdefpriv$/) { - $state = $STATE_PRIV_ERROR; - } else { - &mistake("$file: missing '#ifndef $ifdefpriv'"); - } - } elsif ($state == $STATE_PRIV_ERROR) { - if (/# error ".*"$/) { - $state = $STATE_PRIV_END; - } else { - &mistake("$file: missing '# error ...priv allow...'"); - } - } elsif ($state == $STATE_PRIV_END) { - if (m,#endif /\* $ifdefpriv \*/,) { - $state = $STATE_PRIV_BLANK; - } else { - &mistake("$file: missing '#endif /* $ifdefpriv */'"); - } - } elsif ($state == $STATE_PRIV_BLANK) { - if (! /^$/) { - &mistake("$file: missing blank line after priv guard"); - } - $state = $STATE_GUARD_START; - } elsif ($state == $STATE_GUARD_START) { - if (/^$/) { - &mistake("$file: too many blank lines after copyright header"); - } - if ($publicheader) { - if (/#ifndef $ifdef$/) { - $state = $STATE_GUARD_DEFINE; - } else { - &mistake("$file: missing '#ifndef $ifdef'"); - } - } else { - if (/#pragma once/) { - $state = $STATE_PRAGMA; - } else { - &mistake("$file: missing '#pragma once' header guard"); - } - } - } elsif ($state == $STATE_GUARD_DEFINE) { - if (/# define $ifdef$/) { - $state = $STATE_GUARD_END; - } else { - &mistake("$file: missing '# define $ifdef'"); - } - } elsif ($state == $STATE_GUARD_END) { - if (m,#endif /\* $ifdef \*/$,) { - $state = $STATE_EOF; - } - } elsif ($state == $STATE_PRAGMA) { - next; - } elsif ($state == $STATE_EOF) { - die "$file: unexpected content after '#endif /* $ifdef */'"; - } else { - die "$file: unexpected state $state"; - } -} -exit $ret; diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 7112f8235d..3759c7674f 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -2184,8 +2184,8 @@ mock-noinline: $(PYTHON) $(top_srcdir)/scripts/mock-noinline.py header-ifdef: - $(AM_V_GEN)$(VC_LIST) | $(GREP) '\.[h]$$' | xargs \ - $(PERL) $(top_srcdir)/build-aux/header-ifdef.pl + $(AM_V_GEN)$(VC_LIST) | $(GREP) '\.[h]$$' | $(RUNUTF8) xargs \ + $(PYTHON) $(top_srcdir)/scripts/header-ifdef.py test-wrap-argv: $(AM_V_GEN)$(VC_LIST) | $(GREP) -E '\.(ldargs|args)' | xargs \ diff --git a/scripts/header-ifdef.py b/scripts/header-ifdef.py new file mode 100644 index 0000000000..d5ec7b45fe --- /dev/null +++ b/scripts/header-ifdef.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018-2019 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . +# +# Validate that header files follow a standard layout: +# +# /* +# ...copyright header... +# */ +# +# #pragma once +# ....content.... +# +# --- +# +# For any file ending priv.h, before the #pragma once +# We will have a further section +# +# #ifndef SYMBOL_ALLOW +# # error .... +# #endif /* SYMBOL_ALLOW */ +# +# +# --- +# +# For public headers (files in include/), use the standard +# header guard instead of #pragma once: +# #ifndef SYMBOL +# # define SYMBOL +# ....content.... +# #endif /* SYMBOL */ + +from __future__ import print_function + +import os.path +import re +import sys + +STATE_COPYRIGHT_COMMENT = 0 +STATE_COPYRIGHT_BLANK = 1 +STATE_PRIV_START = 2 +STATE_PRIV_ERROR = 3 +STATE_PRIV_END = 4 +STATE_PRIV_BLANK = 5 +STATE_GUARD_START = 6 +STATE_GUARD_DEFINE = 7 +STATE_GUARD_END = 8 +STATE_EOF = 9 +STATE_PRAGMA = 10 + + +def check_header(filename): + ifdef = "" + ifdefpriv = "" + + state = STATE_EOF + + ifdef = os.path.basename(filename).upper() + ifdef = re.sub(r"""[^A-Z0-9]""", "_", ifdef) + ifdef = re.sub(r"""__+""", "_", ifdef) + + if (not ifdef.startswith("LIBVIRT_") or + "libvirt_internal.h" in filename): + ifdef = "LIBVIRT_" + ifdef + + ifdefpriv = ifdef + "_ALLOW" + + state = STATE_COPYRIGHT_COMMENT + publicheader = False + if "include/" in filename: + publicheader = True + + with open(filename, "r") as fh: + for line in fh: + if state == STATE_COPYRIGHT_COMMENT: + if "*/" in line: + state = STATE_COPYRIGHT_BLANK + elif state == STATE_COPYRIGHT_BLANK: + if not line.isspace(): + print("%s: missing blank line after copyright header" % + filename, file=sys.stderr) + return True + + if filename.endswith("priv.h"): + state = STATE_PRIV_START + else: + state = STATE_GUARD_START + elif state == STATE_PRIV_START: + if line.isspace(): + print("%s: too many blank lines after copyright header" % + filename, file=sys.stderr) + return True + elif re.search(r"""#ifndef %s$""" % ifdefpriv, line): + state = STATE_PRIV_ERROR + else: + print("%s: missing '#ifndef %s'" % (filename, ifdefpriv), + file=sys.stderr) + return True + elif state == STATE_PRIV_ERROR: + if re.search(r"""# error ".*"$""", line): + state = STATE_PRIV_END + else: + print("%s: missing '# error ...priv allow...'" % + filename, file=sys.stderr) + return True + elif state == STATE_PRIV_END: + if re.search(r"""#endif /\* %s \*/""" % ifdefpriv, line): + state = STATE_PRIV_BLANK + else: + print("%s: missing '#endif /* %s */'" % + (filename, ifdefpriv), file=sys.stderr) + return True + elif state == STATE_PRIV_BLANK: + if not line.isspace(): + print("%s: missing blank line after priv guard" % + filename, file=sys.stderr) + return True + state = STATE_GUARD_START + elif state == STATE_GUARD_START: + if line.isspace(): + print("%s: too many blank lines after copyright header" % + filename, file=sys.stderr) + return True + if publicheader: + if re.search(r"""#ifndef %s$""" % ifdef, line): + state = STATE_GUARD_DEFINE + else: + print("%s: missing '#ifndef %s'" % + (filename, ifdef), file=sys.stderr) + return True + else: + if re.search(r"""#pragma once""", line): + state = STATE_PRAGMA + else: + print("%s: missing '#pragma once' header guard" % + filename, file=sys.stderr) + return True + elif state == STATE_GUARD_DEFINE: + if re.search(r"""# define %s$""" % ifdef, line): + state = STATE_GUARD_END + else: + print("%s: missing '# define %s'" % + (filename, ifdef), file=sys.stderr) + return True + elif state == STATE_GUARD_END: + if re.search(r"""#endif /\* %s \*/$""" % ifdef, line): + state = STATE_EOF + elif state == STATE_PRAGMA: + next + elif state == STATE_EOF: + print("%s: unexpected content after '#endif /* %s */'" % + (filename, ifdef), file=sys.stderr) + return True + else: + print("%s: unexpected state $state" % + filename, file=sys.stderr) + return True + + if state == STATE_COPYRIGHT_COMMENT: + print("%s: missing copyright comment" % + filename, file=sys.stderr) + return True + elif state == STATE_COPYRIGHT_BLANK: + print("%s: missing blank line after copyright header" % + filename, file=sys.stderr) + return True + elif state == STATE_PRIV_START: + print("%s: missing '#ifndef %s'" % + (filename, ifdefpriv), file=sys.stderr) + return True + elif state == STATE_PRIV_ERROR: + print("%s: missing '# error ...priv allow...'" % + filename, file=sys.stderr) + return True + elif state == STATE_PRIV_END: + print("%s: missing '#endif /* %s */'" % + (filename, ifdefpriv), file=sys.stderr) + return True + elif state == STATE_PRIV_BLANK: + print("%s: missing blank line after priv header check" % + filename, file=sys.stderr) + return True + elif state == STATE_GUARD_START: + if publicheader: + print("%s: missing '#ifndef %s'" % + (filename, ifdef), file=sys.stderr) + return True + else: + print("%s: missing '#pragma once' header guard" % + filename, file=sys.stderr) + return True + elif state == STATE_GUARD_DEFINE: + print("%s: missing '# define %s'" % + (filename, ifdef), file=sys.stderr) + return True + elif state == STATE_GUARD_END: + print("%s: missing '#endif /* %s */'" % + (filename, ifdef), file=sys.stderr) + return True + + return False + + +ret = 0 + +for filename in sys.argv[1:]: + if "config-post.h" in filename: + continue + if "vbox_CAPI" in filename: + continue + if "vbox_XPCOM" in filename: + continue + if check_header(filename): + ret = 1 + +sys.exit(ret)