1*cc02d7e2SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cc02d7e2SAndroid Build Coastguard Worker 3*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2016 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker# 5*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker# 9*cc02d7e2SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker# 11*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker 17*cc02d7e2SAndroid Build Coastguard Workerimport argparse 18*cc02d7e2SAndroid Build Coastguard Workerimport os 19*cc02d7e2SAndroid Build Coastguard Workerimport os.path 20*cc02d7e2SAndroid Build Coastguard Workerimport re 21*cc02d7e2SAndroid Build Coastguard Workerimport subprocess 22*cc02d7e2SAndroid Build Coastguard Workerimport sys 23*cc02d7e2SAndroid Build Coastguard Worker 24*cc02d7e2SAndroid Build Coastguard Worker 25*cc02d7e2SAndroid Build Coastguard Workerdef build_valid_guard(fpath): 26*cc02d7e2SAndroid Build Coastguard Worker guard_components = ( 27*cc02d7e2SAndroid Build Coastguard Worker fpath.replace("++", "XX").replace(".", "_").upper().split("/") 28*cc02d7e2SAndroid Build Coastguard Worker ) 29*cc02d7e2SAndroid Build Coastguard Worker if fpath.startswith("include/"): 30*cc02d7e2SAndroid Build Coastguard Worker return "_".join(guard_components[1:]) 31*cc02d7e2SAndroid Build Coastguard Worker else: 32*cc02d7e2SAndroid Build Coastguard Worker return "GRPC_" + "_".join(guard_components) 33*cc02d7e2SAndroid Build Coastguard Worker 34*cc02d7e2SAndroid Build Coastguard Worker 35*cc02d7e2SAndroid Build Coastguard Workerdef load(fpath): 36*cc02d7e2SAndroid Build Coastguard Worker with open(fpath, "r") as f: 37*cc02d7e2SAndroid Build Coastguard Worker return f.read() 38*cc02d7e2SAndroid Build Coastguard Worker 39*cc02d7e2SAndroid Build Coastguard Worker 40*cc02d7e2SAndroid Build Coastguard Workerdef save(fpath, contents): 41*cc02d7e2SAndroid Build Coastguard Worker with open(fpath, "w") as f: 42*cc02d7e2SAndroid Build Coastguard Worker f.write(contents) 43*cc02d7e2SAndroid Build Coastguard Worker 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard Workerclass GuardValidator(object): 46*cc02d7e2SAndroid Build Coastguard Worker def __init__(self): 47*cc02d7e2SAndroid Build Coastguard Worker self.ifndef_re = re.compile(r"#ifndef ([A-Z][A-Z_0-9]*)") 48*cc02d7e2SAndroid Build Coastguard Worker self.define_re = re.compile(r"#define ([A-Z][A-Z_0-9]*)") 49*cc02d7e2SAndroid Build Coastguard Worker self.endif_c_core_re = re.compile( 50*cc02d7e2SAndroid Build Coastguard Worker r"#endif /\* (?: *\\\n *)?([A-Z][A-Z_0-9]*) (?:\\\n *)?\*/$" 51*cc02d7e2SAndroid Build Coastguard Worker ) 52*cc02d7e2SAndroid Build Coastguard Worker self.endif_re = re.compile(r"#endif // ([A-Z][A-Z_0-9]*)") 53*cc02d7e2SAndroid Build Coastguard Worker self.comments_then_includes_re = re.compile( 54*cc02d7e2SAndroid Build Coastguard Worker ( 55*cc02d7e2SAndroid Build Coastguard Worker r"^((//.*?$|/\*.*?\*/|[ \r\n\t])*)(([ \r\n\t]|#include" 56*cc02d7e2SAndroid Build Coastguard Worker r" .*)*)(#ifndef [^\n]*\n#define [^\n]*\n)" 57*cc02d7e2SAndroid Build Coastguard Worker ), 58*cc02d7e2SAndroid Build Coastguard Worker re.DOTALL | re.MULTILINE, 59*cc02d7e2SAndroid Build Coastguard Worker ) 60*cc02d7e2SAndroid Build Coastguard Worker self.failed = False 61*cc02d7e2SAndroid Build Coastguard Worker 62*cc02d7e2SAndroid Build Coastguard Worker def _is_c_core_header(self, fpath): 63*cc02d7e2SAndroid Build Coastguard Worker return "include" in fpath and not ( 64*cc02d7e2SAndroid Build Coastguard Worker "grpc++" in fpath 65*cc02d7e2SAndroid Build Coastguard Worker or "grpcpp" in fpath 66*cc02d7e2SAndroid Build Coastguard Worker or "event_engine" in fpath 67*cc02d7e2SAndroid Build Coastguard Worker or fpath.endswith("/grpc_audit_logging.h") 68*cc02d7e2SAndroid Build Coastguard Worker or fpath.endswith("/json.h") 69*cc02d7e2SAndroid Build Coastguard Worker ) 70*cc02d7e2SAndroid Build Coastguard Worker 71*cc02d7e2SAndroid Build Coastguard Worker def fail(self, fpath, regexp, fcontents, match_txt, correct, fix): 72*cc02d7e2SAndroid Build Coastguard Worker c_core_header = self._is_c_core_header(fpath) 73*cc02d7e2SAndroid Build Coastguard Worker self.failed = True 74*cc02d7e2SAndroid Build Coastguard Worker invalid_guards_msg_template = ( 75*cc02d7e2SAndroid Build Coastguard Worker "{0}: Missing preprocessor guards (RE {1}). " 76*cc02d7e2SAndroid Build Coastguard Worker "Please wrap your code around the following guards:\n" 77*cc02d7e2SAndroid Build Coastguard Worker "#ifndef {2}\n" 78*cc02d7e2SAndroid Build Coastguard Worker "#define {2}\n" 79*cc02d7e2SAndroid Build Coastguard Worker "...\n" 80*cc02d7e2SAndroid Build Coastguard Worker "... epic code ...\n" 81*cc02d7e2SAndroid Build Coastguard Worker "...\n" 82*cc02d7e2SAndroid Build Coastguard Worker + ("#endif /* {2} */" if c_core_header else "#endif // {2}") 83*cc02d7e2SAndroid Build Coastguard Worker ) 84*cc02d7e2SAndroid Build Coastguard Worker if not match_txt: 85*cc02d7e2SAndroid Build Coastguard Worker print( 86*cc02d7e2SAndroid Build Coastguard Worker ( 87*cc02d7e2SAndroid Build Coastguard Worker invalid_guards_msg_template.format( 88*cc02d7e2SAndroid Build Coastguard Worker fpath, regexp.pattern, build_valid_guard(fpath) 89*cc02d7e2SAndroid Build Coastguard Worker ) 90*cc02d7e2SAndroid Build Coastguard Worker ) 91*cc02d7e2SAndroid Build Coastguard Worker ) 92*cc02d7e2SAndroid Build Coastguard Worker return fcontents 93*cc02d7e2SAndroid Build Coastguard Worker 94*cc02d7e2SAndroid Build Coastguard Worker print( 95*cc02d7e2SAndroid Build Coastguard Worker ( 96*cc02d7e2SAndroid Build Coastguard Worker ( 97*cc02d7e2SAndroid Build Coastguard Worker "{}: Wrong preprocessor guards (RE {}):" 98*cc02d7e2SAndroid Build Coastguard Worker "\n\tFound {}, expected {}" 99*cc02d7e2SAndroid Build Coastguard Worker ).format(fpath, regexp.pattern, match_txt, correct) 100*cc02d7e2SAndroid Build Coastguard Worker ) 101*cc02d7e2SAndroid Build Coastguard Worker ) 102*cc02d7e2SAndroid Build Coastguard Worker if fix: 103*cc02d7e2SAndroid Build Coastguard Worker print("Fixing {}...\n".format(fpath)) 104*cc02d7e2SAndroid Build Coastguard Worker fixed_fcontents = re.sub(match_txt, correct, fcontents) 105*cc02d7e2SAndroid Build Coastguard Worker if fixed_fcontents: 106*cc02d7e2SAndroid Build Coastguard Worker self.failed = False 107*cc02d7e2SAndroid Build Coastguard Worker return fixed_fcontents 108*cc02d7e2SAndroid Build Coastguard Worker else: 109*cc02d7e2SAndroid Build Coastguard Worker print() 110*cc02d7e2SAndroid Build Coastguard Worker return fcontents 111*cc02d7e2SAndroid Build Coastguard Worker 112*cc02d7e2SAndroid Build Coastguard Worker def check(self, fpath, fix): 113*cc02d7e2SAndroid Build Coastguard Worker c_core_header = self._is_c_core_header(fpath) 114*cc02d7e2SAndroid Build Coastguard Worker valid_guard = build_valid_guard(fpath) 115*cc02d7e2SAndroid Build Coastguard Worker 116*cc02d7e2SAndroid Build Coastguard Worker fcontents = load(fpath) 117*cc02d7e2SAndroid Build Coastguard Worker 118*cc02d7e2SAndroid Build Coastguard Worker match = self.ifndef_re.search(fcontents) 119*cc02d7e2SAndroid Build Coastguard Worker if not match: 120*cc02d7e2SAndroid Build Coastguard Worker print(("something drastically wrong with: %s" % fpath)) 121*cc02d7e2SAndroid Build Coastguard Worker return False # failed 122*cc02d7e2SAndroid Build Coastguard Worker if match.lastindex is None: 123*cc02d7e2SAndroid Build Coastguard Worker # No ifndef. Request manual addition with hints 124*cc02d7e2SAndroid Build Coastguard Worker self.fail(fpath, match.re, match.string, "", "", False) 125*cc02d7e2SAndroid Build Coastguard Worker return False # failed 126*cc02d7e2SAndroid Build Coastguard Worker 127*cc02d7e2SAndroid Build Coastguard Worker # Does the guard end with a '_H'? 128*cc02d7e2SAndroid Build Coastguard Worker running_guard = match.group(1) 129*cc02d7e2SAndroid Build Coastguard Worker if not running_guard.endswith("_H"): 130*cc02d7e2SAndroid Build Coastguard Worker fcontents = self.fail( 131*cc02d7e2SAndroid Build Coastguard Worker fpath, match.re, match.string, match.group(1), valid_guard, fix 132*cc02d7e2SAndroid Build Coastguard Worker ) 133*cc02d7e2SAndroid Build Coastguard Worker if fix: 134*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 135*cc02d7e2SAndroid Build Coastguard Worker 136*cc02d7e2SAndroid Build Coastguard Worker # Is it the expected one based on the file path? 137*cc02d7e2SAndroid Build Coastguard Worker if running_guard != valid_guard: 138*cc02d7e2SAndroid Build Coastguard Worker fcontents = self.fail( 139*cc02d7e2SAndroid Build Coastguard Worker fpath, match.re, match.string, match.group(1), valid_guard, fix 140*cc02d7e2SAndroid Build Coastguard Worker ) 141*cc02d7e2SAndroid Build Coastguard Worker if fix: 142*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 143*cc02d7e2SAndroid Build Coastguard Worker 144*cc02d7e2SAndroid Build Coastguard Worker # Is there a #define? Is it the same as the #ifndef one? 145*cc02d7e2SAndroid Build Coastguard Worker match = self.define_re.search(fcontents) 146*cc02d7e2SAndroid Build Coastguard Worker if match.lastindex is None: 147*cc02d7e2SAndroid Build Coastguard Worker # No define. Request manual addition with hints 148*cc02d7e2SAndroid Build Coastguard Worker self.fail(fpath, match.re, match.string, "", "", False) 149*cc02d7e2SAndroid Build Coastguard Worker return False # failed 150*cc02d7e2SAndroid Build Coastguard Worker 151*cc02d7e2SAndroid Build Coastguard Worker # Is the #define guard the same as the #ifndef guard? 152*cc02d7e2SAndroid Build Coastguard Worker if match.group(1) != running_guard: 153*cc02d7e2SAndroid Build Coastguard Worker fcontents = self.fail( 154*cc02d7e2SAndroid Build Coastguard Worker fpath, match.re, match.string, match.group(1), valid_guard, fix 155*cc02d7e2SAndroid Build Coastguard Worker ) 156*cc02d7e2SAndroid Build Coastguard Worker if fix: 157*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 158*cc02d7e2SAndroid Build Coastguard Worker 159*cc02d7e2SAndroid Build Coastguard Worker # Is there a properly commented #endif? 160*cc02d7e2SAndroid Build Coastguard Worker flines = fcontents.rstrip().splitlines() 161*cc02d7e2SAndroid Build Coastguard Worker # Use findall and use the last result if there are multiple matches, 162*cc02d7e2SAndroid Build Coastguard Worker # i.e. nested include guards. 163*cc02d7e2SAndroid Build Coastguard Worker match = self.endif_c_core_re.findall("\n".join(flines[-3:])) 164*cc02d7e2SAndroid Build Coastguard Worker if not match and not c_core_header: 165*cc02d7e2SAndroid Build Coastguard Worker match = self.endif_re.findall("\n".join(flines[-3:])) 166*cc02d7e2SAndroid Build Coastguard Worker if not match: 167*cc02d7e2SAndroid Build Coastguard Worker # No endif. Check if we have the last line as just '#endif' and if so 168*cc02d7e2SAndroid Build Coastguard Worker # replace it with a properly commented one. 169*cc02d7e2SAndroid Build Coastguard Worker if flines[-1] == "#endif": 170*cc02d7e2SAndroid Build Coastguard Worker flines[-1] = "#endif" + ( 171*cc02d7e2SAndroid Build Coastguard Worker " /* {} */\n".format(valid_guard) 172*cc02d7e2SAndroid Build Coastguard Worker if c_core_header 173*cc02d7e2SAndroid Build Coastguard Worker else " // {}\n".format(valid_guard) 174*cc02d7e2SAndroid Build Coastguard Worker ) 175*cc02d7e2SAndroid Build Coastguard Worker if fix: 176*cc02d7e2SAndroid Build Coastguard Worker fcontents = "\n".join(flines) 177*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 178*cc02d7e2SAndroid Build Coastguard Worker else: 179*cc02d7e2SAndroid Build Coastguard Worker # something else is wrong, bail out 180*cc02d7e2SAndroid Build Coastguard Worker self.fail( 181*cc02d7e2SAndroid Build Coastguard Worker fpath, 182*cc02d7e2SAndroid Build Coastguard Worker self.endif_c_core_re if c_core_header else self.endif_re, 183*cc02d7e2SAndroid Build Coastguard Worker flines[-1], 184*cc02d7e2SAndroid Build Coastguard Worker "", 185*cc02d7e2SAndroid Build Coastguard Worker "", 186*cc02d7e2SAndroid Build Coastguard Worker False, 187*cc02d7e2SAndroid Build Coastguard Worker ) 188*cc02d7e2SAndroid Build Coastguard Worker elif match[-1] != running_guard: 189*cc02d7e2SAndroid Build Coastguard Worker # Is the #endif guard the same as the #ifndef and #define guards? 190*cc02d7e2SAndroid Build Coastguard Worker fcontents = self.fail( 191*cc02d7e2SAndroid Build Coastguard Worker fpath, self.endif_re, fcontents, match[-1], valid_guard, fix 192*cc02d7e2SAndroid Build Coastguard Worker ) 193*cc02d7e2SAndroid Build Coastguard Worker if fix: 194*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 195*cc02d7e2SAndroid Build Coastguard Worker 196*cc02d7e2SAndroid Build Coastguard Worker match = self.comments_then_includes_re.search(fcontents) 197*cc02d7e2SAndroid Build Coastguard Worker assert match 198*cc02d7e2SAndroid Build Coastguard Worker bad_includes = match.group(3) 199*cc02d7e2SAndroid Build Coastguard Worker if bad_includes: 200*cc02d7e2SAndroid Build Coastguard Worker print( 201*cc02d7e2SAndroid Build Coastguard Worker "includes after initial comments but before include guards in", 202*cc02d7e2SAndroid Build Coastguard Worker fpath, 203*cc02d7e2SAndroid Build Coastguard Worker ) 204*cc02d7e2SAndroid Build Coastguard Worker if fix: 205*cc02d7e2SAndroid Build Coastguard Worker fcontents = ( 206*cc02d7e2SAndroid Build Coastguard Worker fcontents[: match.start(3)] 207*cc02d7e2SAndroid Build Coastguard Worker + match.group(5) 208*cc02d7e2SAndroid Build Coastguard Worker + match.group(3) 209*cc02d7e2SAndroid Build Coastguard Worker + fcontents[match.end(5) :] 210*cc02d7e2SAndroid Build Coastguard Worker ) 211*cc02d7e2SAndroid Build Coastguard Worker save(fpath, fcontents) 212*cc02d7e2SAndroid Build Coastguard Worker 213*cc02d7e2SAndroid Build Coastguard Worker return not self.failed # Did the check succeed? (ie, not failed) 214*cc02d7e2SAndroid Build Coastguard Worker 215*cc02d7e2SAndroid Build Coastguard Worker 216*cc02d7e2SAndroid Build Coastguard Worker# find our home 217*cc02d7e2SAndroid Build Coastguard WorkerROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../..")) 218*cc02d7e2SAndroid Build Coastguard Workeros.chdir(ROOT) 219*cc02d7e2SAndroid Build Coastguard Worker 220*cc02d7e2SAndroid Build Coastguard Worker# parse command line 221*cc02d7e2SAndroid Build Coastguard Workerargp = argparse.ArgumentParser(description="include guard checker") 222*cc02d7e2SAndroid Build Coastguard Workerargp.add_argument("-f", "--fix", default=False, action="store_true") 223*cc02d7e2SAndroid Build Coastguard Workerargp.add_argument("--precommit", default=False, action="store_true") 224*cc02d7e2SAndroid Build Coastguard Workerargs = argp.parse_args() 225*cc02d7e2SAndroid Build Coastguard Worker 226*cc02d7e2SAndroid Build Coastguard Workergrep_filter = ( 227*cc02d7e2SAndroid Build Coastguard Worker r"grep -E '^(include|src/core|src/cpp|test/core|test/cpp|fuzztest/)/.*\.h$'" 228*cc02d7e2SAndroid Build Coastguard Worker) 229*cc02d7e2SAndroid Build Coastguard Workerif args.precommit: 230*cc02d7e2SAndroid Build Coastguard Worker git_command = "git diff --name-only HEAD" 231*cc02d7e2SAndroid Build Coastguard Workerelse: 232*cc02d7e2SAndroid Build Coastguard Worker git_command = "git ls-tree -r --name-only -r HEAD" 233*cc02d7e2SAndroid Build Coastguard Worker 234*cc02d7e2SAndroid Build Coastguard WorkerFILE_LIST_COMMAND = " | ".join((git_command, grep_filter)) 235*cc02d7e2SAndroid Build Coastguard Worker 236*cc02d7e2SAndroid Build Coastguard Worker# scan files 237*cc02d7e2SAndroid Build Coastguard Workerok = True 238*cc02d7e2SAndroid Build Coastguard Workerfilename_list = [] 239*cc02d7e2SAndroid Build Coastguard Workertry: 240*cc02d7e2SAndroid Build Coastguard Worker filename_list = ( 241*cc02d7e2SAndroid Build Coastguard Worker subprocess.check_output(FILE_LIST_COMMAND, shell=True) 242*cc02d7e2SAndroid Build Coastguard Worker .decode() 243*cc02d7e2SAndroid Build Coastguard Worker .splitlines() 244*cc02d7e2SAndroid Build Coastguard Worker ) 245*cc02d7e2SAndroid Build Coastguard Worker # Filter out non-existent files (ie, file removed or renamed) 246*cc02d7e2SAndroid Build Coastguard Worker filename_list = (f for f in filename_list if os.path.isfile(f)) 247*cc02d7e2SAndroid Build Coastguard Workerexcept subprocess.CalledProcessError: 248*cc02d7e2SAndroid Build Coastguard Worker sys.exit(0) 249*cc02d7e2SAndroid Build Coastguard Worker 250*cc02d7e2SAndroid Build Coastguard Workervalidator = GuardValidator() 251*cc02d7e2SAndroid Build Coastguard Worker 252*cc02d7e2SAndroid Build Coastguard Workerfor filename in filename_list: 253*cc02d7e2SAndroid Build Coastguard Worker # Skip check for upb generated code. 254*cc02d7e2SAndroid Build Coastguard Worker if ( 255*cc02d7e2SAndroid Build Coastguard Worker filename.endswith(".upb.h") 256*cc02d7e2SAndroid Build Coastguard Worker or filename.endswith(".upbdefs.h") 257*cc02d7e2SAndroid Build Coastguard Worker or filename.endswith(".upbdefs.c") 258*cc02d7e2SAndroid Build Coastguard Worker or filename.endswith(".upb_minitable.h") 259*cc02d7e2SAndroid Build Coastguard Worker or filename.endswith(".upb_minitable.c") 260*cc02d7e2SAndroid Build Coastguard Worker ): 261*cc02d7e2SAndroid Build Coastguard Worker continue 262*cc02d7e2SAndroid Build Coastguard Worker ok = ok and validator.check(filename, args.fix) 263*cc02d7e2SAndroid Build Coastguard Worker 264*cc02d7e2SAndroid Build Coastguard Workersys.exit(0 if ok else 1) 265