Skip to content

Commit fa31908

Browse files
author
MarcoFalke
committed
lint: Check for missing or redundant bitcoin-config.h includes
1 parent fa63b0e commit fa31908

File tree

2 files changed

+108
-2
lines changed

2 files changed

+108
-2
lines changed

ci/lint/04_install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ export PATH=$PWD/ci/retry:$PATH
1010

1111
${CI_RETRY_EXE} apt-get update
1212
# Lint dependencies:
13+
# - automake pkg-config libtool (for lint_includes_build_config)
1314
# - curl/xz-utils (to install shellcheck)
1415
# - git (used in many lint scripts)
1516
# - gpg (used by verify-commits)
16-
${CI_RETRY_EXE} apt-get install -y curl xz-utils git gpg
17+
${CI_RETRY_EXE} apt-get install -y automake pkg-config libtool curl xz-utils git gpg
1718

1819
PYTHON_PATH="/python_build"
1920
if [ ! -d "${PYTHON_PATH}/bin" ]; then

test/lint/test_runner/src/main.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use std::env;
66
use std::fs;
7-
use std::path::PathBuf;
7+
use std::path::{Path, PathBuf};
88
use std::process::Command;
99
use std::process::ExitCode;
1010

@@ -45,6 +45,14 @@ fn get_subtrees() -> Vec<&'static str> {
4545
]
4646
}
4747

48+
/// Return the pathspecs to exclude all subtrees
49+
fn get_pathspecs_exclude_subtrees() -> Vec<String> {
50+
get_subtrees()
51+
.iter()
52+
.map(|s| format!(":(exclude){}", s))
53+
.collect()
54+
}
55+
4856
fn lint_subtree() -> LintResult {
4957
// This only checks that the trees are pure subtrees, it is not doing a full
5058
// check with -r to not have to fetch all the remotes.
@@ -87,6 +95,102 @@ fs:: namespace, which has unsafe filesystem functions marked as deleted.
8795
}
8896
}
8997

98+
fn lint_includes_build_config() -> LintResult {
99+
let config_path = "./src/config/bitcoin-config.h.in";
100+
let include_directive = "#include <config/bitcoin-config.h>";
101+
if !Path::new(config_path).is_file() {
102+
assert!(Command::new("./autogen.sh")
103+
.status()
104+
.expect("command error")
105+
.success());
106+
}
107+
let defines_regex = format!(
108+
r"^\s*(?!//).*({})",
109+
check_output(Command::new("grep").args(["undef ", "--", config_path]))
110+
.expect("grep failed")
111+
.lines()
112+
.map(|line| {
113+
line.split("undef ")
114+
.nth(1)
115+
.unwrap_or_else(|| panic!("Could not extract name in line: {line}"))
116+
})
117+
.collect::<Vec<_>>()
118+
.join("|")
119+
);
120+
let print_affected_files = |mode: bool| {
121+
// * mode==true: Print files which use the define, but lack the include
122+
// * mode==false: Print files which lack the define, but use the include
123+
let defines_files = check_output(
124+
git()
125+
.args([
126+
"grep",
127+
"--perl-regexp",
128+
if mode {
129+
"--files-with-matches"
130+
} else {
131+
"--files-without-match"
132+
},
133+
&defines_regex,
134+
"--",
135+
"*.cpp",
136+
"*.h",
137+
])
138+
.args(get_pathspecs_exclude_subtrees())
139+
.args([
140+
// These are exceptions which don't use bitcoin-config.h, rather the Makefile.am adds
141+
// these cppflags manually.
142+
":(exclude)src/crypto/sha256_arm_shani.cpp",
143+
":(exclude)src/crypto/sha256_avx2.cpp",
144+
":(exclude)src/crypto/sha256_sse41.cpp",
145+
":(exclude)src/crypto/sha256_x86_shani.cpp",
146+
]),
147+
)
148+
.expect("grep failed");
149+
git()
150+
.args([
151+
"grep",
152+
if mode {
153+
"--files-without-match"
154+
} else {
155+
"--files-with-matches"
156+
},
157+
include_directive,
158+
"--",
159+
])
160+
.args(defines_files.lines())
161+
.status()
162+
.expect("command error")
163+
.success()
164+
};
165+
let missing = print_affected_files(true);
166+
if missing {
167+
return Err(format!(
168+
r#"
169+
^^^
170+
One or more files use a symbol declared in the bitcoin-config.h header. However, they are not
171+
including the header. This is problematic, because the header may or may not be indirectly
172+
included. If the indirect include were to be intentionally or accidentally removed, the build could
173+
still succeed, but silently be buggy. For example, a slower fallback algorithm could be picked,
174+
even though bitcoin-config.h indicates that a faster feature is available and should be used.
175+
176+
If you are unsure which symbol is used, you can find it with this command:
177+
git grep --perl-regexp '{}' -- file_name
178+
"#,
179+
defines_regex
180+
));
181+
}
182+
let redundant = print_affected_files(false);
183+
if redundant {
184+
return Err(r#"
185+
^^^
186+
None of the files use a symbol declared in the bitcoin-config.h header. However, they are including
187+
the header. Consider removing the unused include.
188+
"#
189+
.to_string());
190+
}
191+
Ok(())
192+
}
193+
90194
fn lint_doc() -> LintResult {
91195
if Command::new("test/lint/check-doc.py")
92196
.status()
@@ -128,6 +232,7 @@ fn main() -> ExitCode {
128232
let test_list: Vec<(&str, LintFn)> = vec![
129233
("subtree check", lint_subtree),
130234
("std::filesystem check", lint_std_filesystem),
235+
("build config includes check", lint_includes_build_config),
131236
("-help=1 documentation check", lint_doc),
132237
("lint-*.py scripts", lint_all),
133238
];

0 commit comments

Comments
 (0)