|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
# Copyright 2021 gRPC authors.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
import collections
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
def to_inc(filename):
|
|
|
|
"""Given filename, synthesize what should go in an include statement to get that file"""
|
|
|
|
if filename.startswith("include/"):
|
|
|
|
return "<%s>" % filename[len("include/") :]
|
|
|
|
return '"%s"' % filename
|
|
|
|
|
|
|
|
|
|
|
|
def set_pragmas(filename, pragmas):
|
|
|
|
"""Set the file-level IWYU pragma in filename"""
|
|
|
|
lines = []
|
|
|
|
saw_first_define = False
|
|
|
|
for line in open(filename).read().splitlines():
|
|
|
|
if line.startswith("// IWYU pragma: "):
|
|
|
|
continue
|
|
|
|
lines.append(line)
|
|
|
|
if not saw_first_define and line.startswith("#define "):
|
|
|
|
saw_first_define = True
|
|
|
|
lines.append("")
|
|
|
|
for pragma in pragmas:
|
|
|
|
lines.append("// IWYU pragma: %s" % pragma)
|
|
|
|
lines.append("")
|
|
|
|
open(filename, "w").write("\n".join(lines) + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
def set_exports(pub, cg):
|
|
|
|
"""In file pub, mark the include for cg with IWYU pragma: export"""
|
|
|
|
lines = []
|
|
|
|
for line in open(pub).read().splitlines():
|
|
|
|
if line.startswith("#include %s" % to_inc(cg)):
|
|
|
|
lines.append("#include %s // IWYU pragma: export" % to_inc(cg))
|
|
|
|
else:
|
|
|
|
lines.append(line)
|
|
|
|
open(pub, "w").write("\n".join(lines) + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
CG_ROOTS_GRPC = (
|
|
|
|
(r"sync", "grpc/support/sync.h", False),
|
|
|
|
(r"atm", "grpc/support/atm.h", False),
|
|
|
|
(r"grpc_types", "grpc/grpc.h", True),
|
|
|
|
(r"gpr_types", "grpc/grpc.h", True),
|
|
|
|
(r"compression_types", "grpc/compression.h", True),
|
|
|
|
(r"connectivity_state", "grpc/grpc.h", True),
|
|
|
|
)
|
|
|
|
|
|
|
|
CG_ROOTS_GRPCPP = [
|
|
|
|
(r"status_code_enum", "grpcpp/support/status.h", False),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def fix_tree(tree, cg_roots):
|
|
|
|
"""Fix one include tree"""
|
|
|
|
# Map of filename --> paths including that filename
|
|
|
|
reverse_map = collections.defaultdict(list)
|
|
|
|
# The same, but for things with '/impl/codegen' in their names
|
|
|
|
cg_reverse_map = collections.defaultdict(list)
|
|
|
|
for root, dirs, files in os.walk(tree):
|
|
|
|
root_map = cg_reverse_map if "/impl/codegen" in root else reverse_map
|
|
|
|
for filename in files:
|
|
|
|
root_map[filename].append(root)
|
|
|
|
# For each thing in '/impl/codegen' figure out what exports it
|
|
|
|
for filename, paths in cg_reverse_map.items():
|
|
|
|
print("****", filename)
|
|
|
|
# Exclude non-headers
|
|
|
|
if not filename.endswith(".h"):
|
|
|
|
continue
|
|
|
|
pragmas = []
|
|
|
|
# Check for our 'special' headers: if we see one of these, we just
|
|
|
|
# hardcode where they go to because there's some complicated rules.
|
|
|
|
for root, target, friend in cg_roots:
|
|
|
|
print(root, target, friend)
|
|
|
|
if filename.startswith(root):
|
|
|
|
pragmas = ["private, include <%s>" % target]
|
|
|
|
if friend:
|
|
|
|
pragmas.append('friend "src/.*"')
|
|
|
|
if len(paths) == 1:
|
|
|
|
path = paths[0]
|
|
|
|
if filename.startswith(root + "."):
|
|
|
|
set_exports("include/" + target, path + "/" + filename)
|
|
|
|
if filename.startswith(root + "_"):
|
|
|
|
set_exports(
|
|
|
|
path + "/" + root + ".h", path + "/" + filename
|
|
|
|
)
|
|
|
|
# If the path for a file in /impl/codegen is ambiguous, just don't bother
|
|
|
|
if not pragmas and len(paths) == 1:
|
|
|
|
path = paths[0]
|
|
|
|
# Check if we have an exporting candidate
|
|
|
|
if filename in reverse_map:
|
|
|
|
proper = reverse_map[filename]
|
|
|
|
# And that it too is unambiguous
|
|
|
|
if len(proper) == 1:
|
|
|
|
# Build the two relevant pathnames
|
|
|
|
cg = path + "/" + filename
|
|
|
|
pub = proper[0] + "/" + filename
|
|
|
|
# And see if the public file actually includes the /impl/codegen file
|
|
|
|
if ("#include %s" % to_inc(cg)) in open(pub).read():
|
|
|
|
# Finally, if it does, we'll set that pragma
|
|
|
|
pragmas = ["private, include %s" % to_inc(pub)]
|
|
|
|
# And mark the export
|
|
|
|
set_exports(pub, cg)
|
|
|
|
# If we can't find a good alternative include to point people to,
|
|
|
|
# mark things private anyway... we don't want to recommend people include
|
|
|
|
# from impl/codegen
|
|
|
|
if not pragmas:
|
|
|
|
pragmas = ["private"]
|
|
|
|
for path in paths:
|
|
|
|
set_pragmas(path + "/" + filename, pragmas)
|
|
|
|
|
|
|
|
|
|
|
|
fix_tree("include/grpc", CG_ROOTS_GRPC)
|
|
|
|
fix_tree("include/grpcpp", CG_ROOTS_GRPCPP)
|