Warning, file /acts/CI/check_include_guards.py was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002 import argparse
0003 import os
0004 from glob import glob
0005 import re
0006 from fnmatch import fnmatch
0007 import sys
0008
0009
0010 def line_fmt(line):
0011 return "{: >4d} ".format(line)
0012
0013
0014 def code_print(code, start, maxlines=15):
0015 lines = code.split("\n")
0016 nlines = len(lines)
0017
0018 lines = [line_fmt(i + start) + l for i, l in enumerate(lines)]
0019
0020 nlup = int(maxlines / 2)
0021 nllo = maxlines - nlup - 1
0022
0023 if nlines > maxlines:
0024 lines = lines[:nlup] + [" " * 5 + "// ..."] + lines[-nllo:]
0025
0026 return "\n".join(lines)
0027
0028
0029 def check_include_guards(file):
0030 with open(file) as f:
0031 text = f.read()
0032
0033 match_local = list(
0034 re.finditer(
0035 r"(#ifndef [A-Za-z0-9_]*\n#define [A-Za-z0-9_]*.*)\n((:?.|\n)+?)#endif",
0036 text,
0037 )
0038 )
0039 match_global = re.search(
0040 r"#ifndef (.*)\n#define \1.*\n[\s\S]+#endif[A-Za-z0-9\-_/* ]*$", text
0041 )
0042
0043 valid_global = True
0044 valid_local = True
0045 errbuf = ""
0046
0047 if match_global is not None and len(match_local) <= 1:
0048 valid_global = False
0049
0050 errbuf += "This looks like a file-spanning include guard\n"
0051 errbuf += "This is discouraged as per [ACTS-450]"
0052 errbuf += "(https://its.cern.ch/jira/browse/ACTS-450)" + "\n" * 2
0053
0054 start = text[: match_global.start()].count("\n") + 1
0055 errbuf += code_print(match_global.group(0), start)
0056 errbuf += "\n" * 2
0057
0058 if valid_global or len(match_local) > 1:
0059 for m in match_local:
0060 lineno = text[: m.start()].count("\n") + 1
0061
0062 valid_local = False
0063 errbuf += "This looks like a local #ifndef / include-guard\n"
0064 errbuf += "This is discouraged as per [ACTS-450]"
0065 errbuf += "(https://its.cern.ch/jira/browse/ACTS-450)" + "\n" * 2
0066 errbuf += code_print(m.group(0), lineno)
0067 errbuf += "\n" * 2
0068
0069 return valid_local, valid_global, errbuf
0070
0071
0072 def main():
0073 p = argparse.ArgumentParser()
0074
0075 input_help = """
0076 Input files: either file path, dir path (will glob for headers) or custom glob pattern
0077 """
0078 p.add_argument("input", nargs="+", help=input_help.strip())
0079 p.add_argument(
0080 "--fail-local", "-l", action="store_true", help="Fail on local include guards"
0081 )
0082 p.add_argument(
0083 "--fail-global", "-g", action="store_true", help="Fail on global include guards"
0084 )
0085 p.add_argument("--quiet-local", "-ql", action="store_true")
0086 p.add_argument("--quiet-global", "-qg", action="store_true")
0087 p.add_argument("--exclude", "-e", action="append", default=[])
0088
0089 args = p.parse_args()
0090
0091 headers = []
0092
0093 if len(args.input) == 1:
0094 if os.path.isdir(args.input[0]):
0095 patterns = ["**/*.hpp", "**/*.h"]
0096 headers = sum(
0097 [
0098 glob(os.path.join(args.input[0], p), recursive=True)
0099 for p in patterns
0100 ],
0101 [],
0102 )
0103 else:
0104 headers = glob(args.input[0], recursive=True)
0105 elif all([os.path.isfile(i) for i in args.input]):
0106 headers = args.input
0107
0108 valid = True
0109 nlocal = 0
0110 nglobal = 0
0111
0112 for h in headers:
0113 if any([fnmatch(h, e) for e in args.exclude]):
0114 continue
0115 valid_local, valid_global, errbuf = check_include_guards(h)
0116
0117 if not valid_local:
0118 nlocal += 1
0119 if args.fail_local:
0120 valid = False
0121 if not valid_global:
0122 nglobal += 1
0123 if args.fail_global:
0124 valid = False
0125
0126 if not valid_local or not valid_global:
0127 head = "Issue(s) in file {}:\n".format(h)
0128 print("-" * len(head))
0129 print(head)
0130 print(errbuf)
0131 print("\n")
0132
0133 print("=" * 40)
0134 print("Checked {} files".format(len(headers)))
0135 print("Issues found in {} files".format(nlocal + nglobal))
0136 print("{} files have local include guards".format(nlocal))
0137 print("{} files have global include guards".format(nglobal))
0138
0139 if valid:
0140 sys.exit(0)
0141 else:
0142 sys.exit(1)
0143
0144
0145 if "__main__" == __name__:
0146 main()