Mattias Rönnblom, Oct 06, 2024 at 16:17:
I think you need to change run_command() to custom_target(). I was
thinking of this patch to be much simpler as follows:
I started off with a shell script, but I switched to Python when I
wanted to strip off comments. (Not that that can't be done in bourne shell.)
The below script doesn't strip off comments, and provides no usage
information. Earlier I got the impression you wanted to improve
command-line usage.
I think we need to decide if this thing is an integral part of the build
system, custom-made for its needs, or if its something that should also
be friendly to a command-line human user.
I could go either way on that.
We don't have to choose. Being part of the build system does not mean
the script cannot use the standard python tools to parse arguments. Here
is what I came up with. It is shorter, strips comments and deals with
arguments in a simpler way. We don't need to pass the "missing" or
"redundant" operation to the script, it can figure out what to do on its
own (see inline comment in main()):
diff --git a/buildtools/chkincs/check_extern_c.py
b/buildtools/chkincs/check_extern_c.py
new file mode 100755
index 000000000000..3e61719a5ea5
--- /dev/null
+++ b/buildtools/chkincs/check_extern_c.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+
+"""
+Detect missing or redundant `extern "C"` statements in headers.
+"""
+
+import argparse
+import re
+import sys
+
+
+# regular expressions
+EXTERN_C_RE = re.compile(r'^extern\s+"C"', re.MULTILINE)
+CPP_LINE_COMMENT_RE = re.compile(r"//.*$", re.MULTILINE)
+C_BLOCK_COMMENT_RE = re.compile(r"/\*.+?\*/", re.DOTALL)
+C_SYMBOL_RE = [
+ # external variable definitions
+ re.compile(r"^extern\s+[a-z0-9_]+\s", re.MULTILINE),
+ # exported functions
+ re.compile(r"\brte_[a-z0-9_]+\("),
+ re.compile(r"\bcmdline_[a-z0-9_]+\("),
+ re.compile(r"\bvt100_[a-z0-9_]+\("),
+ re.compile(r"\brdline_[a-z0-9_]+\("),
+ re.compile(r"\bcirbuf_[a-z0-9_]+\("),
+ # windows compatibility
+ re.compile(r"\bpthread_[a-z0-9_]+\("),
+ re.compile(r"\bregcomp\("),
+ re.compile(r"\bcount_cpu\("),
+]
+
+
+def strip_comments(buf: str) -> str:
+ buf = CPP_LINE_COMMENT_RE.sub("", buf, re.MULTILINE)
+ return C_BLOCK_COMMENT_RE.sub("", buf, re.DOTALL)
+
+
+def has_c_symbols(buf: str) -> bool:
+ for expr in C_SYMBOL_RE:
+ if expr.search(buf, re.MULTILINE):
+ return True
+ return False
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("headers", nargs="+")
+ args = parser.parse_args()
+
+ ret = 0
+
+ for h in args.headers:
+ with open(h) as f:
+ buf = f.read()
+
+ buf = strip_comments(buf)
+
+ if has_c_symbols(buf):
+ if not EXTERN_C_RE.search(buf):
+ print('error: missing extern "C" in', h)
+ ret = 1
+ # Uncomment next block once all extraneous extern "C" have been removed
+ #else:
+ # if EXTERN_C_RE.search(buf):
+ # print('error: extraneous extern "C" in', h)
+ # ret = 1
+
+ return ret
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/buildtools/chkincs/meson.build b/buildtools/chkincs/meson.build
index f2dadcae18ef..a551b2df9268 100644
--- a/buildtools/chkincs/meson.build
+++ b/buildtools/chkincs/meson.build
@@ -38,14 +38,11 @@ if not add_languages('cpp', required: false)
endif
# check for extern C in files, since this is not detected as an error by the
compiler
-grep = find_program('grep', required: false)
-if grep.found()
- errlist = run_command([grep, '--files-without-match', '^extern "C"',
dpdk_chkinc_headers],
- check: false, capture: true).stdout().split()
- if errlist != []
- error('Files missing C++ \'extern "C"\' guards:\n- ' + '\n-
'.join(errlist))
- endif
-endif
+custom_target('check-extern-c',
+ command: files('check_extern_c.py') + ['@INPUT@'],
+ input: dpdk_chkinc_headers,
+ output: 'check-extern-c',
+ build_by_default: true)
gen_cpp_files = generator(gen_c_file_for_header,
output: '@BASENAME@.cpp',