1#!/usr/bin/env python3 2 3# Copyright 2022 gRPC authors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import argparse 18import os 19import os.path 20import re 21import subprocess 22import sys 23 24# TODO(hork): dedupe args/load/validate/save code with other check scripts. 25 26 27def load(fpath): 28 with open(fpath, "r") as f: 29 return f.readlines() 30 31 32def save(fpath, contents): 33 with open(fpath, "w") as f: 34 f.write(contents) 35 36 37class QualificationValidator(object): 38 def __init__(self): 39 self.fully_qualified_re = re.compile(r"([ (<])::(grpc[A-Za-z_:])") 40 self.using_re = re.compile( 41 r"(using +|using +[A-Za-z_]+ *= *|namespace [A-Za-z_]+ *= *)::" 42 ) 43 self.define_re = re.compile(r"^#define") 44 45 def check(self, fpath, fix): 46 fcontents = load(fpath) 47 failed = False 48 for i, line in enumerate(fcontents): 49 if not self.fully_qualified_re.search(line): 50 continue 51 # skip `using` statements 52 if self.using_re.search(line): 53 continue 54 # skip `#define` statements 55 if self.define_re.search(line): 56 continue 57 # fully-qualified namespace found, which may be unnecessary 58 if fix: 59 fcontents[i] = self.fully_qualified_re.sub(r"\1\2", line) 60 else: 61 print("Found in %s:%d - %s" % (fpath, i, line.strip())) 62 failed = True 63 if fix: 64 save(fpath, "".join(fcontents)) 65 return not failed 66 67 68IGNORED_FILES = [ 69 # TODO(hork): rename symbols to avoid the need for fully-qualified names 70 "src/cpp/common/core_codegen.cc", 71 # TODO(hork): This could be a breaking change for users that define their 72 # own (possibly nested) `grpc.*` namespaces that contain conflicting 73 # symbols. It may be worth trying to land this change at some point, as 74 # users would be better off using unique namespaces. 75 "src/compiler/cpp_generator.cc", 76 # multi-line #define statements are not handled 77 "src/core/lib/profiling/timers.h", 78 "src/core/util/crash.h", 79 "src/core/util/unique_type_name.h", 80 # The grpc_core::Server redundant namespace qualification is required for 81 # older gcc versions. 82 "src/core/ext/transport/chttp2/server/chttp2_server.h", 83 "src/core/server/server.h", 84] 85 86# find our home 87ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../..")) 88os.chdir(ROOT) 89 90# parse command line 91argp = argparse.ArgumentParser( 92 description="c++ namespace full qualification checker" 93) 94argp.add_argument("-f", "--fix", default=False, action="store_true") 95argp.add_argument("--precommit", default=False, action="store_true") 96args = argp.parse_args() 97 98grep_filter = r"grep -E '^(include|src|test).*\.(h|cc)$'" 99if args.precommit: 100 git_command = "git diff --name-only HEAD" 101else: 102 git_command = "git ls-tree -r --name-only -r HEAD" 103 104FILE_LIST_COMMAND = " | ".join((git_command, grep_filter)) 105 106# scan files 107ok = True 108filename_list = [] 109try: 110 filename_list = ( 111 subprocess.check_output(FILE_LIST_COMMAND, shell=True) 112 .decode() 113 .splitlines() 114 ) 115 # Filter out non-existent files (ie, file removed or renamed) 116 filename_list = (f for f in filename_list if os.path.isfile(f)) 117except subprocess.CalledProcessError: 118 sys.exit(0) 119 120validator = QualificationValidator() 121 122for filename in filename_list: 123 # Skip check for upb generated code and ignored files. 124 if ( 125 filename.endswith(".upb.h") 126 or filename.endswith(".upbdefs.h") 127 or filename.endswith(".upbdefs.c") 128 or filename in IGNORED_FILES 129 ): 130 continue 131 ok = validator.check(filename, args.fix) and ok 132 133sys.exit(0 if ok else 1) 134