27 from typing
import List, Tuple
31 logger = logging.getLogger(
"check_header_guards")
34 inside_comment : bool =
False
40 stripped_line : str = line.strip()
41 if stripped_line.startswith(
"/*"):
44 if not inside_comment
and not stripped_line.startswith(
"//")
and stripped_line !=
"":
45 start = min(line_num, start)
48 if inside_comment
and stripped_line.endswith(
"*/"):
49 inside_comment =
False
57 return line.strip().startswith(
"#define")
60 return line.strip().startswith(
"#endif")
63 return line.strip().startswith(
"#ifndef")
67 line = re.sub(
r"/\*.*\*/",
"", line)
68 line = re.sub(
r"//.*",
"", line)
81 return is_ifndef(sline)
and not guard_candidate.startswith(
"__")
and guard_candidate.isupper()
84 def fix_header_guard(lines: List[str], expected_header_guard: str, comment_style: str) -> Tuple[List[str], bool]:
85 start_line, next_line, last_line =
"",
"",
""
87 guards_updated: bool =
True
89 if start_index < len(lines):
91 start_line = lines[start_index]
93 if start_index + 1 < len(lines):
95 next_line = lines[start_index + 1]
97 if last_index < len(lines)
and last_index > start_index + 1:
99 last_line = lines[last_index]
101 expected_start_line = f
"#ifndef {expected_header_guard}\n"
102 expected_next_line = f
"#define {expected_header_guard}\n"
104 if comment_style ==
'double_slash':
105 expected_last_line = f
"#endif // {expected_header_guard}\n"
106 elif comment_style ==
'slash_asterix':
107 expected_last_line = f
"#endif /* {expected_header_guard} */\n"
113 lines = lines[:start_index] + [expected_start_line, expected_next_line] + \
114 lines[start_index+2:last_index] + [expected_last_line] + lines[last_index+1:]
116 guards_updated = (start_line != expected_start_line)
or (next_line != expected_next_line) \
117 or (last_line != expected_last_line)
120 lines = lines[:start_index] + [empty_line, expected_start_line, expected_next_line] + \
121 [empty_line] + lines[start_index:] + [empty_line, expected_last_line]
124 return lines, guards_updated
128 if drop_outermost_subdir:
129 arr : List[str] = filepath.split(
"/")
130 arr = arr[min(1, len(arr)-1):]
131 filepath =
"/".
join(arr)
133 if not add_extension:
134 filepath =
".".
join(filepath.split(
".")[:-1])
136 guard = filepath.replace(
"/",
"_").replace(
".",
"_").upper()
137 return prefix +
"_" + guard
140 def skip_file(filepath: str, extensions: List[str], exclude: List[str], include: List[str]) -> bool:
141 extension = filepath.split(
".")[-1]
143 if extension.lower()
not in extensions:
146 if exclude
and any([filepath.startswith(exc)
for exc
in exclude]):
151 return not any([filepath.startswith(inc)
for inc
in include])
156 if __name__ ==
"__main__":
157 parser = argparse.ArgumentParser(
158 formatter_class=argparse.RawDescriptionHelpFormatter,
159 description=
"Header Guard Checker. It adds full path snake case header guards with or without extension.",
162 parser.add_argument(
"files", type=str, nargs=
"+", help=
"Files to check the header guards")
163 parser.add_argument(
"--extensions", type=str, help=
"Comma separated list of extensions to run the checks. \
164 If the input file does not have any of the extensions, it'll be skipped", required=
True)
165 parser.add_argument(
"--comment_style", choices=[
'double_slash',
'slash_asterix'], required=
True)
166 parser.add_argument(
"--exclude", type=str, help=
"Comma separated list of paths to exclude from header guard checks", default=
"")
167 parser.add_argument(
"--include", type=str, help=
"Comma separated list of paths to include. Defaults to empty string, \
168 which means all the paths are included", default=
"")
169 parser.add_argument(
"--prefix", help=
"Prefix to apply to header guards", required=
True)
170 parser.add_argument(
"--add_extension", action=
"store_true", help=
"If true, it adds the file extension to the end of the guard")
171 parser.add_argument(
"--drop_outermost_subdir", action=
"store_true", help=
"If true, it'll not use the outermost folder in the path. \
172 This is intended for using in subdirs with different rules")
174 args = parser.parse_args()
177 extensions = args.extensions.split(
",")
178 exclude = args.exclude.split(
",")
if args.exclude !=
'' else []
179 include = args.include.split(
",")
if args.include !=
'' else []
181 add_extension = args.add_extension
182 drop_outermost_subdir = args.drop_outermost_subdir
183 comment_style = args.comment_style
185 logging_level = logging.INFO
186 logging.basicConfig(level=logging_level)
190 if skip_file(file, extensions, exclude, include):
191 logger.info(f
"File {file} is SKIPPED")
196 with open(file,
"r")
as fd:
197 lines: List = fd.readlines()
201 with open(file,
"w")
as fd:
202 fd.writelines([f
"{line}" for line
in new_lines])
205 logger.info(
"File has been modified")