Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
check_include_guards.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file check_include_guards.py
1 #!/usr/bin/env python3
2 from __future__ import print_function
3 
4 import argparse
5 import os
6 from glob import glob
7 from concurrent.futures import ProcessPoolExecutor
8 import re
9 from fnmatch import fnmatch
10 import sys
11 
12 
13 def line_fmt(line):
14  return "{: >4d} ".format(line)
15 
16 
17 def code_print(code, start, maxlines=15):
18  lines = code.split("\n")
19  nlines = len(lines)
20 
21  lines = [line_fmt(i + start) + l for i, l in enumerate(lines)]
22 
23  nlup = int(maxlines / 2)
24  nllo = maxlines - nlup - 1
25 
26  if nlines > maxlines:
27  lines = lines[:nlup] + [" " * 5 + "// ..."] + lines[-nllo:]
28 
29  return "\n".join(lines)
30 
31 
33  with open(file) as f:
34  text = f.read()
35 
36  match_local = list(
37  re.finditer(
38  r"(#ifndef [A-Za-z0-9_]*\n#define [A-Za-z0-9_]*.*)\n((:?.|\n)+?)#endif",
39  text,
40  )
41  )
42  match_global = re.search(
43  r"#ifndef (.*)\n#define \1.*\n[\s\S]+#endif[A-Za-z0-9\-_/* ]*$", text
44  )
45 
46  valid_global = True
47  valid_local = True
48  errbuf = ""
49 
50  if match_global is not None and len(match_local) <= 1:
51  valid_global = False
52 
53  errbuf += "This looks like a file-spanning include guard\n"
54  errbuf += "This is discouraged as per [ACTS-450]"
55  errbuf += "(https://its.cern.ch/jira/browse/ACTS-450)" + "\n" * 2
56 
57  start = text[: match_global.start()].count("\n") + 1
58  errbuf += code_print(match_global.group(0), start)
59  errbuf += "\n" * 2
60 
61  if valid_global or len(match_local) > 1:
62  for m in match_local:
63 
64  lineno = text[: m.start()].count("\n") + 1
65 
66  valid_local = False
67  errbuf += "This looks like a local #ifndef / include-guard\n"
68  errbuf += "This is discouraged as per [ACTS-450]"
69  errbuf += "(https://its.cern.ch/jira/browse/ACTS-450)" + "\n" * 2
70  errbuf += code_print(m.group(0), lineno)
71  errbuf += "\n" * 2
72 
73  return valid_local, valid_global, errbuf
74 
75 
76 def main():
77  p = argparse.ArgumentParser()
78 
79  input_help = """
80 Input files: either file path, dir path (will glob for headers) or custom glob pattern
81  """
82  p.add_argument("input", help=input_help.strip())
83  p.add_argument(
84  "--fail-local", "-l", action="store_true", help="Fail on local include guards"
85  )
86  p.add_argument(
87  "--fail-global", "-g", action="store_true", help="Fail on global include guards"
88  )
89  p.add_argument("--quiet-local", "-ql", action="store_true")
90  p.add_argument("--quiet-global", "-qg", action="store_true")
91  p.add_argument("--exclude", "-e", action="append", default=[])
92 
93  args = p.parse_args()
94 
95  headers = []
96 
97  if os.path.isfile(args.input):
98  headers = [args.input]
99  elif os.path.isdir(args.input):
100  patterns = ["**/*.hpp", "**/*.h"]
101  headers = sum(
102  [glob(os.path.join(args.input, p), recursive=True) for p in patterns], []
103  )
104  else:
105  headers = glob(args.input, recursive=True)
106 
107  valid = True
108  nlocal = 0
109  nglobal = 0
110 
111  for h in headers:
112  if any([fnmatch(h, e) for e in args.exclude]):
113  continue
114  valid_local, valid_global, errbuf = check_include_guards(h)
115 
116  if not valid_local:
117  nlocal += 1
118  if args.fail_local:
119  valid = False
120  if not valid_global:
121  nglobal += 1
122  if args.fail_global:
123  valid = False
124 
125  if not valid_local or not valid_global:
126  head = "Issue(s) in file {}:\n".format(h)
127  print("-" * len(head))
128  print(head)
129  print(errbuf)
130  print("\n")
131 
132  print("=" * 40)
133  print("Checked {} files".format(len(headers)))
134  print("Issues found in {} files".format(nlocal + nglobal))
135  print("{} files have local include guards".format(nlocal))
136  print("{} files have global include guards".format(nglobal))
137 
138  if valid:
139  sys.exit(0)
140  else:
141  sys.exit(1)
142 
143 
144 if "__main__" == __name__:
145  main()