Skip to content

Commit 67b4167

Browse files
committed
lint: Convert lint-includes.sh to Python
1 parent d1b3dfb commit 67b4167

File tree

2 files changed

+179
-103
lines changed

2 files changed

+179
-103
lines changed

test/lint/lint-includes.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2018-2022 The Bitcoin Core developers
4+
# Distributed under the MIT software license, see the accompanying
5+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
6+
#
7+
# Check for duplicate includes.
8+
# Guard against accidental introduction of new Boost dependencies.
9+
# Check includes: Check for duplicate includes. Enforce bracket syntax includes.
10+
11+
import os
12+
import re
13+
import sys
14+
15+
from subprocess import check_output, CalledProcessError
16+
17+
18+
EXCLUDED_DIRS = ["src/leveldb/",
19+
"src/crc32c/",
20+
"src/secp256k1/",
21+
"src/minisketch/",
22+
"src/univalue/"]
23+
24+
EXPECTED_BOOST_INCLUDES = ["boost/algorithm/string.hpp",
25+
"boost/algorithm/string/classification.hpp",
26+
"boost/algorithm/string/replace.hpp",
27+
"boost/algorithm/string/split.hpp",
28+
"boost/date_time/posix_time/posix_time.hpp",
29+
"boost/multi_index/hashed_index.hpp",
30+
"boost/multi_index/ordered_index.hpp",
31+
"boost/multi_index/sequenced_index.hpp",
32+
"boost/multi_index_container.hpp",
33+
"boost/process.hpp",
34+
"boost/signals2/connection.hpp",
35+
"boost/signals2/optional_last_value.hpp",
36+
"boost/signals2/signal.hpp",
37+
"boost/test/included/unit_test.hpp",
38+
"boost/test/unit_test.hpp"]
39+
40+
41+
def get_toplevel():
42+
return check_output(["git", "rev-parse", "--show-toplevel"], universal_newlines=True, encoding="utf8").rstrip("\n")
43+
44+
45+
def list_files_by_suffix(suffixes):
46+
exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
47+
48+
files_list = check_output(["git", "ls-files", "src"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
49+
50+
return [file for file in files_list if file.endswith(suffixes)]
51+
52+
53+
def find_duplicate_includes(include_list):
54+
tempset = set()
55+
duplicates = set()
56+
57+
for inclusion in include_list:
58+
if inclusion in tempset:
59+
duplicates.add(inclusion)
60+
else:
61+
tempset.add(inclusion)
62+
63+
return duplicates
64+
65+
66+
def find_included_cpps():
67+
included_cpps = list()
68+
69+
try:
70+
included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
71+
except CalledProcessError as e:
72+
if e.returncode > 1:
73+
raise e
74+
75+
return included_cpps
76+
77+
78+
def find_extra_boosts():
79+
included_boosts = list()
80+
filtered_included_boost_set = set()
81+
exclusion_set = set()
82+
83+
try:
84+
included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
85+
except CalledProcessError as e:
86+
if e.returncode > 1:
87+
raise e
88+
89+
for boost in included_boosts:
90+
filtered_included_boost_set.add(re.findall(r'(?<=\<).+?(?=\>)', boost)[0])
91+
92+
for expected_boost in EXPECTED_BOOST_INCLUDES:
93+
for boost in filtered_included_boost_set:
94+
if expected_boost in boost:
95+
exclusion_set.add(boost)
96+
97+
extra_boosts = set(filtered_included_boost_set.difference(exclusion_set))
98+
99+
return extra_boosts
100+
101+
102+
def find_quote_syntax_inclusions():
103+
exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
104+
quote_syntax_inclusions = list()
105+
106+
try:
107+
quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
108+
except CalledProcessError as e:
109+
if e.returncode > 1:
110+
raise e
111+
112+
return quote_syntax_inclusions
113+
114+
115+
def main():
116+
exit_code = 0
117+
118+
os.chdir(get_toplevel())
119+
120+
# Check for duplicate includes
121+
for filename in list_files_by_suffix((".cpp", ".h")):
122+
with open(filename, "r", encoding="utf8") as file:
123+
include_list = [line.rstrip("\n") for line in file if re.match(r"^#include", line)]
124+
125+
duplicates = find_duplicate_includes(include_list)
126+
127+
if duplicates:
128+
print(f"Duplicate include(s) in {filename}:")
129+
for duplicate in duplicates:
130+
print(duplicate)
131+
print("")
132+
exit_code = 1
133+
134+
# Check if code includes .cpp-files
135+
included_cpps = find_included_cpps()
136+
137+
if included_cpps:
138+
print("The following files #include .cpp files:")
139+
for included_cpp in included_cpps:
140+
print(included_cpp)
141+
print("")
142+
exit_code = 1
143+
144+
# Guard against accidental introduction of new Boost dependencies
145+
extra_boosts = find_extra_boosts()
146+
147+
if extra_boosts:
148+
for boost in extra_boosts:
149+
print(f"A new Boost dependency in the form of \"{boost}\" appears to have been introduced:")
150+
print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8"))
151+
exit_code = 1
152+
153+
# Check if Boost dependencies are no longer used
154+
for expected_boost in EXPECTED_BOOST_INCLUDES:
155+
try:
156+
check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8")
157+
except CalledProcessError as e:
158+
if e.returncode > 1:
159+
raise e
160+
else:
161+
print(f"Good job! The Boost dependency \"{expected_boost}\" is no longer used. "
162+
"Please remove it from EXPECTED_BOOST_INCLUDES in test/lint/lint-includes.py "
163+
"to make sure this dependency is not accidentally reintroduced.\n")
164+
exit_code = 1
165+
166+
# Enforce bracket syntax includes
167+
quote_syntax_inclusions = find_quote_syntax_inclusions()
168+
169+
if quote_syntax_inclusions:
170+
print("Please use bracket syntax includes (\"#include <foo.h>\") instead of quote syntax includes:")
171+
for quote_syntax_inclusion in quote_syntax_inclusions:
172+
print(quote_syntax_inclusion)
173+
exit_code = 1
174+
175+
sys.exit(exit_code)
176+
177+
178+
if __name__ == "__main__":
179+
main()

test/lint/lint-includes.sh

Lines changed: 0 additions & 103 deletions
This file was deleted.

0 commit comments

Comments
 (0)