From nobody Sun Feb 8 05:41:06 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1568219311; cv=none; d=zoho.com; s=zohoarc; b=VeTYszQ8zFLHM+V+2xySk7Z3qOKkBL0zqEuUG7ffXHnWlutly1BfO34PC4QRy+4JO0/Hi9xE6U97Q6l9X+Rq9rJHipPSACRGwKslcpy6ViTwGYkUoSumDRnIa77WbPAjf8iceuoZVhglgYVLEOzp7kSi0I2+C3xGRXObkoztHKc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1568219311; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=AYjI7qznyw2/Hn5/ZmLod4bWAB4qkjRFVAR5sMJ57KI=; b=lGO2h3a6LX6s8Yhcphzs4R/Y68O9qxruSna6F8MB0Vke746YH6A54imqigc5ZCual6CvOkSTq+CDtt6ILwwtAiimS3viVgDx7I7kZKQJTRMznpX5snRVd4qqUpBojH2S+xnKQZxLtaS4KSzSMzyJ0P5fIy5+pureIyOJHpAjdFw= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1568219311532360.52568209495416; Wed, 11 Sep 2019 09:28:31 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B969A7F75E; Wed, 11 Sep 2019 16:28:29 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8D3A11001938; Wed, 11 Sep 2019 16:28:29 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 3D02124F36; Wed, 11 Sep 2019 16:28:29 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x8BGSHvQ013096 for ; Wed, 11 Sep 2019 12:28:17 -0400 Received: by smtp.corp.redhat.com (Postfix) id 3EC886012A; Wed, 11 Sep 2019 16:28:17 +0000 (UTC) Received: from catbus.gsslab.fab.redhat.com (dhcp-32.gsslab.fab.redhat.com [10.33.9.32]) by smtp.corp.redhat.com (Postfix) with ESMTP id 80A9B60167; Wed, 11 Sep 2019 16:28:16 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: libvir-list@redhat.com Date: Wed, 11 Sep 2019 17:23:16 +0100 Message-Id: <20190911162333.8668-8-berrange@redhat.com> In-Reply-To: <20190911162333.8668-1-berrange@redhat.com> References: <20190911162333.8668-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH v2 07/24] build-aux: rewrite whitespace checker in Python X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.6.2 (mx1.redhat.com [10.5.110.71]); Wed, 11 Sep 2019 16:28:30 +0000 (UTC) As part of an goal to eliminate Perl from libvirt build tools, rewrite the check-spacing.pl tool in Python. This was a straight conversion, manually going line-by-line to change the syntax from Perl to Python. Thus the overall structure of the file and approach is the same. Signed-off-by: Daniel P. Berrang=C3=A9 --- Makefile.am | 2 +- build-aux/check-spacing.pl | 198 ----------------------------------- build-aux/check-spacing.py | 204 +++++++++++++++++++++++++++++++++++++ cfg.mk | 4 +- 4 files changed, 207 insertions(+), 201 deletions(-) delete mode 100755 build-aux/check-spacing.pl create mode 100755 build-aux/check-spacing.py diff --git a/Makefile.am b/Makefile.am index a7d8da7146..03bf1beb78 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,7 +42,7 @@ EXTRA_DIST =3D \ README.md \ AUTHORS.in \ build-aux/augeas-gentest.py \ - build-aux/check-spacing.pl \ + build-aux/check-spacing.py \ build-aux/gitlog-to-changelog \ build-aux/header-ifdef.pl \ build-aux/minimize-po.py \ diff --git a/build-aux/check-spacing.pl b/build-aux/check-spacing.pl deleted file mode 100755 index 33377f3dd3..0000000000 --- a/build-aux/check-spacing.pl +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env perl -# -# check-spacing.pl: Report any usage of 'function (..args..)' -# Also check for other syntax issues, such as correct use of ';' -# -# 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 -# . - -use strict; -use warnings; - -my $ret =3D 0; -my $incomment =3D 0; - -foreach my $file (@ARGV) { - # Per-file variables for multiline Curly Bracket (cb_) check - my $cb_linenum =3D 0; - my $cb_code =3D ""; - my $cb_scolon =3D 0; - - open FILE, $file; - - while (defined (my $line =3D )) { - my $data =3D $line; - # For temporary modifications - my $tmpdata; - - # Kill any quoted , ; =3D or " - $data =3D~ s/'[";,=3D]'/'X'/g; - - # Kill any quoted strings - $data =3D~ s,"(?:[^\\\"]|\\.)*","XXX",g; - - next if $data =3D~ /^#/; - - # Kill contents of multi-line comments - # and detect end of multi-line comments - if ($incomment) { - if ($data =3D~ m,\*/,) { - $incomment =3D 0; - $data =3D~ s,^.*\*/,*/,; - } else { - $data =3D ""; - } - } - - # Kill single line comments, and detect - # start of multi-line comments - if ($data =3D~ m,/\*.*\*/,) { - $data =3D~ s,/\*.*\*/,/* */,; - } elsif ($data =3D~ m,/\*,) { - $incomment =3D 1; - $data =3D~ s,/\*.*,/*,; - } - - # We need to match things like - # - # int foo (int bar, bool wizz); - # foo (bar, wizz); - # - # but not match things like: - # - # typedef int (*foo)(bar wizz) - # - # we can't do this (efficiently) without - # missing things like - # - # foo (*bar, wizz); - # - # We also don't want to spoil the $data so it can be used - # later on. - $tmpdata =3D $data; - while ($tmpdata =3D~ /(\w+)\s\((?!\*)/) { - my $kw =3D $1; - - # Allow space after keywords only - if ($kw =3D~ /^(?:if|for|while|switch|return)$/) { - $tmpdata =3D~ s/(?:$kw\s\()/XXX(/; - } else { - print "Whitespace after non-keyword:\n"; - print "$file:$.: $line"; - $ret =3D 1; - last; - } - } - - # Require whitespace immediately after keywords - if ($data =3D~ /\b(?:if|for|while|switch|return)\(/) { - print "No whitespace after keyword:\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # Forbid whitespace between )( of a function typedef - if ($data =3D~ /\(\*\w+\)\s+\(/) { - print "Whitespace between ')' and '(':\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # Forbid whitespace following ( or prior to ) - # but allow whitespace before ) on a single line - # (optionally followed by a semicolon) - if (($data =3D~ /\s\)/ && not $data =3D~ /^\s+\);?$/) || - $data =3D~ /\((?!$)\s/) { - print "Whitespace after '(' or before ')':\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # Forbid whitespace before ";" or ",". Things like below are allow= ed: - # - # 1) The expression is empty for "for" loop. E.g. - # for (i =3D 0; ; i++) - # - # 2) An empty statement. E.g. - # while (write(statuswrite, &status, 1) =3D=3D -1 && - # errno =3D=3D EINTR) - # ; - # - if ($data =3D~ /\s[;,]/) { - unless ($data =3D~ /\S; ; / || - $data =3D~ /^\s+;/) { - print "Whitespace before semicolon or comma:\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - } - - # Require EOL, macro line continuation, or whitespace after ";". - # Allow "for (;;)" as an exception. - if ($data =3D~ /;[^ \\\n;)]/) { - print "Invalid character after semicolon:\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # Require EOL, space, or enum/struct end after comma. - if ($data =3D~ /,[^ \\\n)}]/) { - print "Invalid character after comma:\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # Require spaces around assignment '=3D', compounds and '=3D=3D' - if ($data =3D~ /[^ ]\b[!<>&|\-+*\/%\^=3D]?=3D/ || - $data =3D~ /=3D[^=3D \\\n]/) { - print "Spacing around '=3D' or '=3D=3D':\n"; - print "$file:$.: $line"; - $ret =3D 1; - } - - # One line conditional statements with one line bodies should - # not use curly brackets. - if ($data =3D~ /^\s*(if|while|for)\b.*\{$/) { - $cb_linenum =3D $.; - $cb_code =3D $line; - $cb_scolon =3D 0; - } - - # We need to check for exactly one semicolon inside the body, - # because empty statements (e.g. with comment only) are - # allowed - if ($cb_linenum =3D=3D $. - 1 && $data =3D~ /^[^;]*;[^;]*$/) { - $cb_code .=3D $line; - $cb_scolon =3D 1; - } - - if ($data =3D~ /^\s*}\s*$/ && - $cb_linenum =3D=3D $. - 2 && - $cb_scolon) { - - print "Curly brackets around single-line body:\n"; - print "$file:$cb_linenum-$.:\n$cb_code$line"; - $ret =3D 1; - - # There _should_ be no need to reset the values; but to - # keep my inner peace... - $cb_linenum =3D 0; - $cb_scolon =3D 0; - $cb_code =3D ""; - } - } - close FILE; -} - -exit $ret; diff --git a/build-aux/check-spacing.py b/build-aux/check-spacing.py new file mode 100755 index 0000000000..ec565585ad --- /dev/null +++ b/build-aux/check-spacing.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# Copyright (C) 2012-2019 Red Hat, Inc. +# +# check-spacing.pl: Report any usage of 'function (..args..)' +# Also check for other syntax issues, such as correct use of ';' +# +# 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 +# . + +from __future__ import print_function + +import re +import sys + +def check_whitespace(filename): + errs =3D False + with open(filename, 'r') as fh: + quotedmetaprog =3D re.compile(r"""'[";,=3D]'""") + quotedstringprog =3D re.compile(r'''"(?:[^\\\"]|\\.)*"''') + commentstartprog =3D re.compile(r'''^(.*)/\*.*$''') + commentendprog =3D re.compile(r'''^.*\*/(.*)$''') + commentprog =3D re.compile(r'''^(.*)/\*.*\*/(.*)''') + funcprog =3D re.compile(r'''(\w+)\s\((?!\*)''') + keywordprog =3D re.compile(r'''^.*\b(?:if|for|while|switch|return)= \(.*$''') + functypedefprog =3D re.compile(r'''^.*\(\*\w+\)\s+\(.*$''') + whitespaceprog1 =3D re.compile(r'''^.*\s\).*$''') + whitespaceprog2 =3D re.compile(r'''^\s+\);?$''') + whitespaceprog3 =3D re.compile(r'''^.*\((?!$)\s.*''') + commasemiprog1 =3D re.compile(r'''.*\s[;,].*''') + commasemiprog2 =3D re.compile(r'''.*\S; ; .*''') + commasemiprog3 =3D re.compile(r'''^\s+;''') + semicolonprog =3D re.compile(r'''.*;[^ \\\n;)].*''') + commaprog =3D re.compile(r'''.*,[^ \\\n)}].*''') + assignprog1 =3D re.compile(r'''[^ ]\b[!<>&|\-+*\/%\^=3D]?=3D''') + assignprog2 =3D re.compile(r'''=3D[^=3D \\\n]''') + condstartprog =3D re.compile(r'''^\s*(if|while|for)\b.*\{$''') + statementprog =3D re.compile(r'''^[^;]*;[^;]*$''') + condendprog =3D re.compile(r'''^\s*}\s*$''') + + incomment =3D False + # Per-file variables for multiline Curly Bracket (cb_) check + cb_lineno =3D 0 + cb_code =3D "" + cb_scolon =3D False + + lineno =3D 0 + for line in fh: + lineno =3D lineno + 1 + data =3D line + # For temporary modifications + + # Kill any quoted , ; =3D or " + data =3D quotedmetaprog.sub("'X'", data) + + # Kill any quoted strings + data =3D quotedstringprog.sub('"XXX"', data) + + if data[0] =3D=3D '#': + continue + + # Kill contents of multi-line comments + # and detect end of multi-line comments + if incomment: + if commentendprog.match(data): + data =3D commentendprog.sub('*/\2', data) + incomment =3D False + else: + data =3D "" + + # Kill single line comments, and detect + # start of multi-line comments + if commentprog.match(data): + data =3D commentprog.sub(r'''\1/* */\2''', data) + elif commentstartprog.match(data): + data =3D commentstartprog.sub(r'''\1/*''', data) + incomment =3D True + + # We need to match things like + # + # int foo (int bar, bool wizz); + # foo (bar, wizz); + # + # but not match things like: + # + # typedef int (*foo)(bar wizz) + # + # we can't do this (efficiently) without + # missing things like + # + # foo (*bar, wizz); + # + for match in funcprog.finditer(data): + kw =3D match.group(1) + + # Allow space after keywords only + if kw not in ["if", "for", "while", "switch", "return"]: + print("Whitespace after non-keyword:", file=3Dsys.stde= rr) + print("%s:%d: %s" % (filename, lineno, line), file=3Ds= ys.stderr) + errs =3D True + break + + # Require whitespace immediately after keywords + if keywordprog.match(data): + print("No whitespace after keyword:", file=3Dsys.stderr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Forbid whitespace between )( of a function typedef + if functypedefprog.match(data): + print("Whitespace between ')' and '(':", file=3Dsys.stderr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Forbid whitespace following ( or prior to ) + # but allow whitespace before ) on a single line + # (optionally followed by a semicolon) + if ((whitespaceprog1.match(data) and + not whitespaceprog2.match(data)) + or whitespaceprog3.match(data)): + print("Whitespace after '(' or before ')':", file=3Dsys.st= derr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Forbid whitespace before ";" or ",". Things like below are a= llowed: + # + # 1) The expression is empty for "for" loop. E.g. + # for (i =3D 0; ; i++) + # + # 2) An empty statement. E.g. + # while (write(statuswrite, &status, 1) =3D=3D -1 && + # errno =3D=3D EINTR) + # ; + # + if commasemiprog1.match(data) and not ( + commasemiprog2.match(data) or + commasemiprog3.match(data)): + print("Whitespace before semicolon or comma:", file=3Dsys.= stderr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Require EOL, macro line continuation, or whitespace after ";= ". + # Allow "for (;;)" as an exception. + if semicolonprog.match(data): + print("Invalid character after semicolon:", file=3Dsys.std= err) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Require EOL, space, or enum/struct end after comma. + if commaprog.match(data): + print("Invalid character after comma:", file=3Dsys.stderr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # Require spaces around assignment '=3D', compounds and '=3D= =3D' + if assignprog1.match(data) or assignprog2.match(data): + print("Spacing around '=3D' or '=3D=3D':", file=3Dsys.stde= rr) + print("%s:%d: %s" % (filename, lineno, line), file=3Dsys.s= tderr) + errs =3D True + + # One line conditional statements with one line bodies should + # not use curly brackets. + if condstartprog.match(data): + cb_lineno =3D lineno + cb_code =3D line + cb_scolon =3D False + + # We need to check for exactly one semicolon inside the body, + # because empty statements (e.g. with comment only) are + # allowed + if (cb_lineno =3D=3D lineno - 1) and statementprog.match(data): + cb_code =3D cb_code + line + cb_scolon =3D True + + if condendprog.match(data) and (cb_lineno =3D=3D lineno - 2) a= nd cb_scolon: + print("Curly brackets around single-line body:", file=3Dsy= s.stderr) + print("%s:%d:\n%s%s"% (filename, cb_linenum - lineno, cb_c= ode, line), file=3Dsys.stderr) + errs =3D True + + # There _should_ be no need to reset the values; but to + # keep my inner peace... + cb_linenum =3D 0 + cb_scolon =3D False + cb_code =3D "" + + return errs + +ret =3D 0 +for filename in sys.argv[1:]: + if check_whitespace(filename): + ret =3D 1 + +sys.exit(ret) diff --git a/cfg.mk b/cfg.mk index 933857bc2f..4da3554b30 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1151,8 +1151,8 @@ prohibit-duplicate-header: $(PYTHON) $(top_srcdir)/build-aux/prohibit-duplicate-header.py =20 spacing-check: - $(AM_V_GEN)$(VC_LIST) | $(GREP) '\.c$$' | xargs \ - $(PERL) $(top_srcdir)/build-aux/check-spacing.pl || \ + $(AM_V_GEN)$(VC_LIST) | $(GREP) '\.c$$' | $(RUNUTF8) xargs \ + $(PYTHON) $(top_srcdir)/build-aux/check-spacing.py || \ { echo '$(ME): incorrect formatting' 1>&2; exit 1; } =20 mock-noinline: --=20 2.21.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list