1*cc02d7e2SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cc02d7e2SAndroid Build Coastguard Worker 3*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2021 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 collections 18*cc02d7e2SAndroid Build Coastguard Workerimport os 19*cc02d7e2SAndroid Build Coastguard Worker 20*cc02d7e2SAndroid Build Coastguard Worker 21*cc02d7e2SAndroid Build Coastguard Workerdef to_inc(filename): 22*cc02d7e2SAndroid Build Coastguard Worker """Given filename, synthesize what should go in an include statement to get that file""" 23*cc02d7e2SAndroid Build Coastguard Worker if filename.startswith("include/"): 24*cc02d7e2SAndroid Build Coastguard Worker return "<%s>" % filename[len("include/") :] 25*cc02d7e2SAndroid Build Coastguard Worker return '"%s"' % filename 26*cc02d7e2SAndroid Build Coastguard Worker 27*cc02d7e2SAndroid Build Coastguard Worker 28*cc02d7e2SAndroid Build Coastguard Workerdef set_pragmas(filename, pragmas): 29*cc02d7e2SAndroid Build Coastguard Worker """Set the file-level IWYU pragma in filename""" 30*cc02d7e2SAndroid Build Coastguard Worker lines = [] 31*cc02d7e2SAndroid Build Coastguard Worker saw_first_define = False 32*cc02d7e2SAndroid Build Coastguard Worker for line in open(filename).read().splitlines(): 33*cc02d7e2SAndroid Build Coastguard Worker if line.startswith("// IWYU pragma: "): 34*cc02d7e2SAndroid Build Coastguard Worker continue 35*cc02d7e2SAndroid Build Coastguard Worker lines.append(line) 36*cc02d7e2SAndroid Build Coastguard Worker if not saw_first_define and line.startswith("#define "): 37*cc02d7e2SAndroid Build Coastguard Worker saw_first_define = True 38*cc02d7e2SAndroid Build Coastguard Worker lines.append("") 39*cc02d7e2SAndroid Build Coastguard Worker for pragma in pragmas: 40*cc02d7e2SAndroid Build Coastguard Worker lines.append("// IWYU pragma: %s" % pragma) 41*cc02d7e2SAndroid Build Coastguard Worker lines.append("") 42*cc02d7e2SAndroid Build Coastguard Worker open(filename, "w").write("\n".join(lines) + "\n") 43*cc02d7e2SAndroid Build Coastguard Worker 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard Workerdef set_exports(pub, cg): 46*cc02d7e2SAndroid Build Coastguard Worker """In file pub, mark the include for cg with IWYU pragma: export""" 47*cc02d7e2SAndroid Build Coastguard Worker lines = [] 48*cc02d7e2SAndroid Build Coastguard Worker for line in open(pub).read().splitlines(): 49*cc02d7e2SAndroid Build Coastguard Worker if line.startswith("#include %s" % to_inc(cg)): 50*cc02d7e2SAndroid Build Coastguard Worker lines.append("#include %s // IWYU pragma: export" % to_inc(cg)) 51*cc02d7e2SAndroid Build Coastguard Worker else: 52*cc02d7e2SAndroid Build Coastguard Worker lines.append(line) 53*cc02d7e2SAndroid Build Coastguard Worker open(pub, "w").write("\n".join(lines) + "\n") 54*cc02d7e2SAndroid Build Coastguard Worker 55*cc02d7e2SAndroid Build Coastguard Worker 56*cc02d7e2SAndroid Build Coastguard WorkerCG_ROOTS_GRPC = ( 57*cc02d7e2SAndroid Build Coastguard Worker (r"sync", "grpc/support/sync.h", False), 58*cc02d7e2SAndroid Build Coastguard Worker (r"atm", "grpc/support/atm.h", False), 59*cc02d7e2SAndroid Build Coastguard Worker (r"grpc_types", "grpc/grpc.h", True), 60*cc02d7e2SAndroid Build Coastguard Worker (r"gpr_types", "grpc/grpc.h", True), 61*cc02d7e2SAndroid Build Coastguard Worker (r"compression_types", "grpc/compression.h", True), 62*cc02d7e2SAndroid Build Coastguard Worker (r"connectivity_state", "grpc/grpc.h", True), 63*cc02d7e2SAndroid Build Coastguard Worker) 64*cc02d7e2SAndroid Build Coastguard Worker 65*cc02d7e2SAndroid Build Coastguard WorkerCG_ROOTS_GRPCPP = [ 66*cc02d7e2SAndroid Build Coastguard Worker (r"status_code_enum", "grpcpp/support/status.h", False), 67*cc02d7e2SAndroid Build Coastguard Worker] 68*cc02d7e2SAndroid Build Coastguard Worker 69*cc02d7e2SAndroid Build Coastguard Worker 70*cc02d7e2SAndroid Build Coastguard Workerdef fix_tree(tree, cg_roots): 71*cc02d7e2SAndroid Build Coastguard Worker """Fix one include tree""" 72*cc02d7e2SAndroid Build Coastguard Worker # Map of filename --> paths including that filename 73*cc02d7e2SAndroid Build Coastguard Worker reverse_map = collections.defaultdict(list) 74*cc02d7e2SAndroid Build Coastguard Worker # The same, but for things with '/impl/codegen' in their names 75*cc02d7e2SAndroid Build Coastguard Worker cg_reverse_map = collections.defaultdict(list) 76*cc02d7e2SAndroid Build Coastguard Worker for root, dirs, files in os.walk(tree): 77*cc02d7e2SAndroid Build Coastguard Worker root_map = cg_reverse_map if "/impl/codegen" in root else reverse_map 78*cc02d7e2SAndroid Build Coastguard Worker for filename in files: 79*cc02d7e2SAndroid Build Coastguard Worker root_map[filename].append(root) 80*cc02d7e2SAndroid Build Coastguard Worker # For each thing in '/impl/codegen' figure out what exports it 81*cc02d7e2SAndroid Build Coastguard Worker for filename, paths in cg_reverse_map.items(): 82*cc02d7e2SAndroid Build Coastguard Worker print("****", filename) 83*cc02d7e2SAndroid Build Coastguard Worker # Exclude non-headers 84*cc02d7e2SAndroid Build Coastguard Worker if not filename.endswith(".h"): 85*cc02d7e2SAndroid Build Coastguard Worker continue 86*cc02d7e2SAndroid Build Coastguard Worker pragmas = [] 87*cc02d7e2SAndroid Build Coastguard Worker # Check for our 'special' headers: if we see one of these, we just 88*cc02d7e2SAndroid Build Coastguard Worker # hardcode where they go to because there's some complicated rules. 89*cc02d7e2SAndroid Build Coastguard Worker for root, target, friend in cg_roots: 90*cc02d7e2SAndroid Build Coastguard Worker print(root, target, friend) 91*cc02d7e2SAndroid Build Coastguard Worker if filename.startswith(root): 92*cc02d7e2SAndroid Build Coastguard Worker pragmas = ["private, include <%s>" % target] 93*cc02d7e2SAndroid Build Coastguard Worker if friend: 94*cc02d7e2SAndroid Build Coastguard Worker pragmas.append('friend "src/.*"') 95*cc02d7e2SAndroid Build Coastguard Worker if len(paths) == 1: 96*cc02d7e2SAndroid Build Coastguard Worker path = paths[0] 97*cc02d7e2SAndroid Build Coastguard Worker if filename.startswith(root + "."): 98*cc02d7e2SAndroid Build Coastguard Worker set_exports("include/" + target, path + "/" + filename) 99*cc02d7e2SAndroid Build Coastguard Worker if filename.startswith(root + "_"): 100*cc02d7e2SAndroid Build Coastguard Worker set_exports( 101*cc02d7e2SAndroid Build Coastguard Worker path + "/" + root + ".h", path + "/" + filename 102*cc02d7e2SAndroid Build Coastguard Worker ) 103*cc02d7e2SAndroid Build Coastguard Worker # If the path for a file in /impl/codegen is ambiguous, just don't bother 104*cc02d7e2SAndroid Build Coastguard Worker if not pragmas and len(paths) == 1: 105*cc02d7e2SAndroid Build Coastguard Worker path = paths[0] 106*cc02d7e2SAndroid Build Coastguard Worker # Check if we have an exporting candidate 107*cc02d7e2SAndroid Build Coastguard Worker if filename in reverse_map: 108*cc02d7e2SAndroid Build Coastguard Worker proper = reverse_map[filename] 109*cc02d7e2SAndroid Build Coastguard Worker # And that it too is unambiguous 110*cc02d7e2SAndroid Build Coastguard Worker if len(proper) == 1: 111*cc02d7e2SAndroid Build Coastguard Worker # Build the two relevant pathnames 112*cc02d7e2SAndroid Build Coastguard Worker cg = path + "/" + filename 113*cc02d7e2SAndroid Build Coastguard Worker pub = proper[0] + "/" + filename 114*cc02d7e2SAndroid Build Coastguard Worker # And see if the public file actually includes the /impl/codegen file 115*cc02d7e2SAndroid Build Coastguard Worker if ("#include %s" % to_inc(cg)) in open(pub).read(): 116*cc02d7e2SAndroid Build Coastguard Worker # Finally, if it does, we'll set that pragma 117*cc02d7e2SAndroid Build Coastguard Worker pragmas = ["private, include %s" % to_inc(pub)] 118*cc02d7e2SAndroid Build Coastguard Worker # And mark the export 119*cc02d7e2SAndroid Build Coastguard Worker set_exports(pub, cg) 120*cc02d7e2SAndroid Build Coastguard Worker # If we can't find a good alternative include to point people to, 121*cc02d7e2SAndroid Build Coastguard Worker # mark things private anyway... we don't want to recommend people include 122*cc02d7e2SAndroid Build Coastguard Worker # from impl/codegen 123*cc02d7e2SAndroid Build Coastguard Worker if not pragmas: 124*cc02d7e2SAndroid Build Coastguard Worker pragmas = ["private"] 125*cc02d7e2SAndroid Build Coastguard Worker for path in paths: 126*cc02d7e2SAndroid Build Coastguard Worker set_pragmas(path + "/" + filename, pragmas) 127*cc02d7e2SAndroid Build Coastguard Worker 128*cc02d7e2SAndroid Build Coastguard Worker 129*cc02d7e2SAndroid Build Coastguard Workerfix_tree("include/grpc", CG_ROOTS_GRPC) 130*cc02d7e2SAndroid Build Coastguard Workerfix_tree("include/grpcpp", CG_ROOTS_GRPCPP) 131