Skip to content

Commit eaede27

Browse files
committed
Merge bitcoin/bitcoin#29408: lint: Check for missing bitcoin-config.h includes
fa58ae7 refactor: Add missing include for USE_BDB, USE_SQLITE to bench/wallet_ismine.cpp (MarcoFalke) fa31908 lint: Check for missing or redundant bitcoin-config.h includes (MarcoFalke) fa63b0e lint: Make lint error easier to spot in output (MarcoFalke) fa770fd doc: Add missing RUST_BACKTRACE=1 (MarcoFalke) fa10051 lint: Add get_subtrees() helper (MarcoFalke) Pull request description: Missing `bitcoin-config.h` includes are problematic, because the build could silently pass, but produce an unintended result. For example, a slower fallback algorithm could be picked, even though `bitcoin-config.h` indicates that a faster feature is available and should be used. As the build succeeds silently, this problem is not possible to detect with iwyu. Thus, fix this by using a linter based on grepping the source code. ACKs for top commit: theuni: Weak ACK fa58ae7. TheCharlatan: ACK fa58ae7 hebasto: ACK fa58ae7, tested on Ubuntu 23.10 -- it catches bugs properly. I didn't review rust code changes. Tree-SHA512: cf4346f81ea5b8c215da6004cb2403d1aaf569589613c305d8ba00329b82b3841da94fe1a69815ce15f2edecbef9b031758ec9b6433564976190e3cf91ec8181
2 parents bd1c66f + fa58ae7 commit eaede27

File tree

4 files changed

+126
-12
lines changed

4 files changed

+126
-12
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

src/bench/wallet_ismine.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#if defined(HAVE_CONFIG_H)
6+
#include <config/bitcoin-config.h>
7+
#endif // HAVE_CONFIG_H
58
#include <bench/bench.h>
69
#include <interfaces/chain.h>
710
#include <key.h>
@@ -11,9 +14,9 @@
1114
#include <util/translation.h>
1215
#include <validationinterface.h>
1316
#include <wallet/context.h>
17+
#include <wallet/test/util.h>
1418
#include <wallet/wallet.h>
1519
#include <wallet/walletutil.h>
16-
#include <wallet/test/util.h>
1720

1821
namespace wallet {
1922
static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_combo = 0)

test/lint/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ test runner
1919
To run all the lint checks in the test runner outside the docker, use:
2020

2121
```sh
22-
( cd ./test/lint/test_runner/ && cargo fmt && cargo clippy && cargo run )
22+
( cd ./test/lint/test_runner/ && cargo fmt && cargo clippy && RUST_BACKTRACE=1 cargo run )
2323
```
2424

2525
#### Dependencies

test/lint/test_runner/src/main.rs

Lines changed: 119 additions & 9 deletions
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

@@ -34,17 +34,30 @@ fn get_git_root() -> PathBuf {
3434
PathBuf::from(check_output(git().args(["rev-parse", "--show-toplevel"])).unwrap())
3535
}
3636

37+
/// Return all subtree paths
38+
fn get_subtrees() -> Vec<&'static str> {
39+
vec![
40+
"src/crc32c",
41+
"src/crypto/ctaes",
42+
"src/leveldb",
43+
"src/minisketch",
44+
"src/secp256k1",
45+
]
46+
}
47+
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+
3756
fn lint_subtree() -> LintResult {
3857
// This only checks that the trees are pure subtrees, it is not doing a full
3958
// check with -r to not have to fetch all the remotes.
4059
let mut good = true;
41-
for subtree in [
42-
"src/crypto/ctaes",
43-
"src/secp256k1",
44-
"src/minisketch",
45-
"src/leveldb",
46-
"src/crc32c",
47-
] {
60+
for subtree in get_subtrees() {
4861
good &= Command::new("test/lint/git-subtree-check.sh")
4962
.arg(subtree)
5063
.status()
@@ -82,6 +95,102 @@ fs:: namespace, which has unsafe filesystem functions marked as deleted.
8295
}
8396
}
8497

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+
85194
fn lint_doc() -> LintResult {
86195
if Command::new("test/lint/check-doc.py")
87196
.status()
@@ -123,6 +232,7 @@ fn main() -> ExitCode {
123232
let test_list: Vec<(&str, LintFn)> = vec![
124233
("subtree check", lint_subtree),
125234
("std::filesystem check", lint_std_filesystem),
235+
("build config includes check", lint_includes_build_config),
126236
("-help=1 documentation check", lint_doc),
127237
("lint-*.py scripts", lint_all),
128238
];
@@ -134,7 +244,7 @@ fn main() -> ExitCode {
134244
// chdir to root before each lint test
135245
env::set_current_dir(&git_root).unwrap();
136246
if let Err(err) = lint_fn() {
137-
println!("{err}\n^---- Failure generated from {lint_name}!");
247+
println!("{err}\n^---- ⚠️ Failure generated from {lint_name}!");
138248
test_failed = true;
139249
}
140250
}

0 commit comments

Comments
 (0)