xref: /aosp_15_r20/external/grpc-grpc/tools/distrib/add-iwyu.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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