|
|
|
#!/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.
|
|
|
|
|
|
|
|
# Eliminate the kind of redundant namespace qualifiers that tend to
|
|
|
|
# creep in when converting C to C++.
|
|
|
|
|
|
|
|
import collections
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
def find_closing_mustache(contents, initial_depth):
|
|
|
|
"""Find the closing mustache for a given number of open mustaches."""
|
|
|
|
depth = initial_depth
|
|
|
|
start_len = len(contents)
|
|
|
|
while contents:
|
|
|
|
# Skip over strings.
|
|
|
|
if contents[0] == '"':
|
|
|
|
contents = contents[1:]
|
|
|
|
while contents[0] != '"':
|
|
|
|
if contents.startswith("\\\\"):
|
|
|
|
contents = contents[2:]
|
|
|
|
elif contents.startswith('\\"'):
|
|
|
|
contents = contents[2:]
|
|
|
|
else:
|
|
|
|
contents = contents[1:]
|
|
|
|
contents = contents[1:]
|
|
|
|
# And characters that might confuse us.
|
|
|
|
elif (
|
|
|
|
contents.startswith("'{'")
|
|
|
|
or contents.startswith("'\"'")
|
|
|
|
or contents.startswith("'}'")
|
|
|
|
):
|
|
|
|
contents = contents[3:]
|
|
|
|
# Skip over comments.
|
|
|
|
elif contents.startswith("//"):
|
|
|
|
contents = contents[contents.find("\n") :]
|
|
|
|
elif contents.startswith("/*"):
|
|
|
|
contents = contents[contents.find("*/") + 2 :]
|
|
|
|
# Count up or down if we see a mustache.
|
|
|
|
elif contents[0] == "{":
|
|
|
|
contents = contents[1:]
|
|
|
|
depth += 1
|
|
|
|
elif contents[0] == "}":
|
|
|
|
contents = contents[1:]
|
|
|
|
depth -= 1
|
|
|
|
if depth == 0:
|
|
|
|
return start_len - len(contents)
|
|
|
|
# Skip over everything else.
|
|
|
|
else:
|
|
|
|
contents = contents[1:]
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def is_a_define_statement(match, body):
|
|
|
|
"""See if the matching line begins with #define"""
|
|
|
|
# This does not yet help with multi-line defines
|
|
|
|
m = re.search(
|
|
|
|
r"^#define.*{}$".format(match.group(0)),
|
|
|
|
body[: match.end()],
|
|
|
|
re.MULTILINE,
|
|
|
|
)
|
|
|
|
return m is not None
|
|
|
|
|
|
|
|
|
|
|
|
def update_file(contents, namespaces):
|
|
|
|
"""Scan the contents of a file, and for top-level namespaces in namespaces remove redundant usages."""
|
|
|
|
output = ""
|
|
|
|
while contents:
|
|
|
|
m = re.search(r"namespace ([a-zA-Z0-9_]*) {", contents)
|
|
|
|
if not m:
|
|
|
|
output += contents
|
|
|
|
break
|
|
|
|
output += contents[: m.end()]
|
|
|
|
contents = contents[m.end() :]
|
|
|
|
end = find_closing_mustache(contents, 1)
|
|
|
|
if end is None:
|
|
|
|
print(
|
|
|
|
"Failed to find closing mustache for namespace {}".format(
|
|
|
|
m.group(1)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
print("Remaining text:")
|
|
|
|
print(contents)
|
|
|
|
sys.exit(1)
|
|
|
|
body = contents[:end]
|
|
|
|
namespace = m.group(1)
|
|
|
|
if namespace in namespaces:
|
|
|
|
while body:
|
|
|
|
# Find instances of 'namespace::'
|
|
|
|
m = re.search(r"\b" + namespace + r"::\b", body)
|
|
|
|
if not m:
|
|
|
|
break
|
|
|
|
# Ignore instances of '::namespace::' -- these are usually meant to be there.
|
|
|
|
if m.start() >= 2 and body[m.start() - 2 :].startswith("::"):
|
|
|
|
output += body[: m.end()]
|
|
|
|
# Ignore #defines, since they may be used anywhere
|
|
|
|
elif is_a_define_statement(m, body):
|
|
|
|
output += body[: m.end()]
|
|
|
|
else:
|
|
|
|
output += body[: m.start()]
|
|
|
|
body = body[m.end() :]
|
|
|
|
output += body
|
|
|
|
contents = contents[end:]
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
|
|
|
# self check before doing anything
|
|
|
|
_TEST = """
|
|
|
|
namespace bar {
|
|
|
|
namespace baz {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
namespace foo {}
|
|
|
|
namespace foo {
|
|
|
|
foo::a;
|
|
|
|
::foo::a;
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
_TEST_EXPECTED = """
|
|
|
|
namespace bar {
|
|
|
|
namespace baz {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
namespace foo {}
|
|
|
|
namespace foo {
|
|
|
|
a;
|
|
|
|
::foo::a;
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
output = update_file(_TEST, ["foo"])
|
|
|
|
if output != _TEST_EXPECTED:
|
|
|
|
import difflib
|
|
|
|
|
|
|
|
print("FAILED: self check")
|
|
|
|
print(
|
|
|
|
"\n".join(
|
|
|
|
difflib.ndiff(_TEST_EXPECTED.splitlines(1), output.splitlines(1))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# Main loop.
|
|
|
|
Config = collections.namedtuple("Config", ["dirs", "namespaces"])
|
|
|
|
|
|
|
|
_CONFIGURATION = (Config(["src/core", "test/core"], ["grpc_core"]),)
|
|
|
|
|
|
|
|
changed = []
|
|
|
|
|
|
|
|
for config in _CONFIGURATION:
|
|
|
|
for dir in config.dirs:
|
|
|
|
for root, dirs, files in os.walk(dir):
|
|
|
|
for file in files:
|
|
|
|
if file.endswith(".cc") or file.endswith(".h"):
|
|
|
|
path = os.path.join(root, file)
|
|
|
|
try:
|
|
|
|
with open(path) as f:
|
|
|
|
contents = f.read()
|
|
|
|
except IOError:
|
|
|
|
continue
|
|
|
|
updated = update_file(contents, config.namespaces)
|
|
|
|
if updated != contents:
|
|
|
|
changed.append(path)
|
|
|
|
with open(os.path.join(root, file), "w") as f:
|
|
|
|
f.write(updated)
|
|
|
|
|
|
|
|
if changed:
|
|
|
|
print("The following files were changed:")
|
|
|
|
for path in changed:
|
|
|
|
print(" " + path)
|
|
|
|
sys.exit(1)
|