linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alexey Dobriyan <adobriyan@gmail.com>
To: torvalds@linux-foundation.org
Cc: linux-kernel@vger.kernel.org, akpm@linux-foundation.org
Subject: [PATCH 11/11] pragma once: conversion script (in Python 2)
Date: Sun, 28 Feb 2021 20:05:49 +0300	[thread overview]
Message-ID: <YDvNbbHd2cKXlLme@localhost.localdomain> (raw)
In-Reply-To: <YDvLYzsGu+l1pQ2y@localhost.localdomain>

From 2bffcdfec69a8d28e9cb2c535724fbba8e12b820 Mon Sep 17 00:00:00 2001
From: Alexey Dobriyan <adobriyan@gmail.com>
Date: Tue, 9 Feb 2021 14:47:34 +0300
Subject: [PATCH 11/11] pragma once: conversion script (in Python 2)

Script accepts list of files to be converted from the command line,
strips include guard if any and inserts "#pragma once" directive in
the beginning.

The following patterns are recognised:

	#ifndef FOO_H		#ifndef FOO_H
	#define FOO_H		#ifndef FOO_H 1

	#endif
	#endif // comment
	#endif /* one line comment */

This is how almost all include guards look like.

Scripts doesn't pretend to be a compiler. For example, comments inside
preprocessor directive aren't recognised because people don't write code
like this:

	# /*
	   * legal C
	   */        def\
	ine FOO /*
		 * no, we don't care
		 */

Trailing multiline comments aren't recognised as well.

Script can cut through SPDX comments:

	/* SPDX-License-Identifier: xxx
	 *	<=== pragma once will be inserted here
	 * Copyright ...
	 */

In other words, the script is simple but not too simple.

It cowardly exits and doesn't do anything as a safety measure in case of
an existing pragma once directive, missing/broken include guard or a bug.
Running it second time shouldn't do anything.

Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
 scripts/pragma-once.py | 159 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 159 insertions(+)
 create mode 100755 scripts/pragma-once.py

diff --git a/scripts/pragma-once.py b/scripts/pragma-once.py
new file mode 100755
index 000000000000..7c8a274aad28
--- /dev/null
+++ b/scripts/pragma-once.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python2
+# Copyright (c) 2021 Alexey Dobriyan <adobriyan@gmail.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Change include guard to "#pragma once" directive in place.
+import os
+import re
+import sys
+
+def read_file(filename):
+    with open(filename) as f:
+        return f.read()
+
+def write_file(filename, buf):
+    tmp = '%s.pragma-once' % filename
+    with open(tmp, 'w') as f:
+        f.write(buf)
+    os.rename(tmp, filename)
+
+def ws(c):
+    return c == ' ' or c == '\t' or c == '\n'
+
+re_ifndef = re.compile('#[ \t]*ifndef[ \t]+([A-Za-z_][A-Za-z0-9_]*)\n')
+re_define = re.compile('#[ \t]*define[ \t]+([A-Za-z_][A-Za-z0-9_]*)([ \t]+1)?\n')
+re_endif1 = re.compile('#[ \t]*endif[ \t]*//')
+re_endif2 = re.compile('#[ \t]*endif[ \t]*/\*')
+
+def pragma_once(c):
+    i = 0
+
+    # skip leading whitespace and comments
+    while i < len(c):
+        if ws(c[i]):
+            i += 1
+        elif c[i] == '/' and c[i + 1] == '*':
+            i = c.index('*/', i + 2) + 2
+        elif c[i] == '/' and c[i + 1] == '/':
+            i = c.index('\n', i + 2) + 1
+        else:
+            break;
+
+    # find #ifndef
+    ifndef_start = i
+    match = re_ifndef.match(c, i)
+    guard = match.group(1)
+    i = match.end()
+
+    # find #define
+    match = re_define.match(c, i)
+    if match.group(1) != guard:
+        raise
+    i = match.end()
+
+    while ws(c[i]):
+        i += 1
+
+    define_end = i
+
+    # trim whitespace before #ifndef
+    i = ifndef_start
+    while i > 0 and ws(c[i - 1]):
+        i -= 1
+    if c[i] == '\n':
+        i += 1
+    ifndef_start = i
+
+    #print repr(c[ifndef_start:define_end])
+
+    # find #endif
+    i = c.rindex('\n#endif', i) + 1
+    endif_start = i
+
+    match = None
+    if match is None:
+        match = re_endif1.match(c, i)
+        if match:
+            try:
+                i = c.index('\n', match.end()) + 1
+            except ValueError:
+                i = len(c)
+
+    if match is None:
+        match = re_endif2.match(c, i)
+        if match:
+            try:
+                i = c.index('*/', match.end()) + 2
+            except ValueError:
+                i = len(c)
+
+    if match is None:
+        i = endif_start + len('#endif')
+
+    while i < len(c) and ws(c[i]):
+        i += 1
+    if i != len(c):
+        raise
+
+    endif_end = i
+
+    # trim whitespace before #endif
+    i = endif_start
+    while i > 0 and ws(c[i - 1]):
+        i -= 1
+    if c[i] == '\n':
+        i += 1
+    endif_start = i
+
+    #print repr(c[endif_start:endif_end])
+
+    if define_end > endif_start:
+        raise
+
+    spdx_end = None
+    pragma_once = '#pragma once\n'
+    cut_comment = False
+    if c.startswith('/* SPDX'):
+        spdx_end = c.index('\n') + 1
+        if not (c[spdx_end - 3] == '*' and c[spdx_end - 2] == '/'):
+            cut_comment = True
+    elif c.startswith('// SPDX') or c.startswith('//SPDX'):
+        spdx_end = c.index('\n') + 1
+
+    if spdx_end is None:
+        l = [pragma_once, c[0:ifndef_start]]
+    elif cut_comment:
+        l = [c[0:spdx_end - 1], ' */\n', pragma_once, '/*\n', c[spdx_end:ifndef_start]]
+    else:
+        l = [c[0:spdx_end], pragma_once, c[spdx_end:ifndef_start]]
+
+    l.append(c[define_end:endif_start])
+    l.append(c[endif_end:])
+    return ''.join(l)
+
+def main():
+    for filename in sys.argv[1:]:
+        s = ''
+        try:
+            buf = read_file(filename)
+            if buf.find('#pragma once') == -1:
+                write_file(filename, pragma_once(buf))
+            else:
+                s = 'skip '
+        except:
+            pass
+        print '#pragma once: %s%s' % (s, filename)
+
+if __name__ == '__main__':
+    main()
-- 
2.29.2


  parent reply	other threads:[~2021-02-28 17:06 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-28 16:57 [PATCH 00/11] pragma once: treewide conversion Alexey Dobriyan
2021-02-28 16:58 ` [PATCH 01/11] pragma once: delete include/linux/atm_suni.h Alexey Dobriyan
2021-02-28 19:05   ` Jakub Kicinski
2021-02-28 16:59 ` [PATCH 02/11] pragma once: convert arch/arm/tools/gen-mach-types Alexey Dobriyan
2021-03-01 10:19   ` Russell King - ARM Linux admin
2021-03-02 15:15     ` Alexey Dobriyan
2021-02-28 16:59 ` [PATCH 03/11] pragma once: convert arch/s390/tools/gen_facilities.c Alexey Dobriyan
2021-02-28 17:00 ` [PATCH 04/11] pragma once: convert drivers/gpu/drm/pl111/pl111_nomadik.h Alexey Dobriyan
2021-03-01 14:41   ` Linus Walleij
2021-02-28 17:01 ` [PATCH 05/11] pragma once: convert drivers/scsi/qla2xxx/qla_target.h Alexey Dobriyan
2021-02-28 22:07   ` Bart Van Assche
2021-02-28 17:02 ` [PATCH 06/11] pragma once: convert include/linux/cb710.h Alexey Dobriyan
2021-03-03 23:13   ` Michał Mirosław
2021-02-28 17:02 ` [PATCH 07/11] pragma once: convert kernel/time/timeconst.bc Alexey Dobriyan
2021-02-28 17:03 ` [PATCH 08/11] pragma once: convert scripts/atomic/ Alexey Dobriyan
2021-03-01  7:55   ` Peter Zijlstra
2021-02-28 17:04 ` [PATCH 09/11] pragma once: convert scripts/selinux/genheaders/genheaders.c Alexey Dobriyan
2021-02-28 18:37   ` Paul Moore
2021-02-28 18:57     ` Alexey Dobriyan
2021-02-28 17:05 ` [PATCH 10/11] pragma once: delete few backslashes Alexey Dobriyan
2021-03-01  8:54   ` Ido Schimmel
2021-03-02 19:00   ` Vineet Gupta
2021-03-04 14:22   ` Edward Cree
2021-03-23 10:09     ` Pavel Machek
2021-02-28 17:05 ` Alexey Dobriyan [this message]
2021-02-28 17:11 ` [PATCH 12/11] pragma once: scripted treewide conversion Alexey Dobriyan
2021-03-01 17:35   ` Darrick J. Wong
2021-02-28 17:46 ` [PATCH 00/11] pragma once: " Linus Torvalds
2021-02-28 19:34   ` Alexey Dobriyan
2021-02-28 20:00     ` Linus Torvalds
     [not found]       ` <877dmo10m3.fsf@tromey.com>
2021-03-03 20:17         ` Linus Torvalds
2021-03-04 13:55           ` David Laight
2021-03-04 20:16             ` Linus Torvalds
2021-03-05  9:19               ` David Laight
2021-03-05 21:23                 ` Linus Torvalds
2021-03-06 13:07                   ` Miguel Ojeda
2021-03-06 21:33                     ` Linus Torvalds
2021-03-23 10:03               ` Pavel Machek
2021-03-01  0:29     ` Luc Van Oostenryck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YDvNbbHd2cKXlLme@localhost.localdomain \
    --to=adobriyan@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).