Skip to content

Commit b2603f8

Browse files
committed
rust: warn on bindgen < 0.69.5 and libclang >= 19.1
When testing a `clang` upgrade with Rust Binder, Alice encountered [1] a build failure caused by `bindgen` not translating some symbols related to tracepoints. This was caused by commit 2e770edd8ce1 ("[libclang] Compute the right spelling location") changing the behavior of a function exposed by `libclang`. `bindgen` fixed the regression in commit 600f63895f73 ("Use clang_getFileLocation instead of clang_getSpellingLocation"). However, the regression fix is only available in `bindgen` versions 0.69.5 or later (it was backported for 0.69.x). This means that when older bindgen versions are used with new versions of `libclang`, `bindgen` may do the wrong thing, which could lead to a build failure. Alice encountered the bug with some header files related to tracepoints, but it could also cause build failures in other circumstances. Thus, always emit a warning when using an old `bindgen` with a new `libclang` so that other people do not have to spend time chasing down the same bug. However, testing just the version is inconvenient, since distributions do patch their packages without changing the version, so I reduced the issue into the following piece of code that can trigger the issue: #define F(x) int x##x F(foo); In particular, an unpatched `bindgen` will ignore the macro expansion and thus not provide a declaration for the exported `int`. Thus add a build test to `rust_is_available.sh` using the code above (that is only triggered if the versions appear to be affected), following what we did for the 0.66.x issue. Moreover, I checked the status in the major distributions we have instructions for: - Fedora 41 was affected but is now OK, since it now ships `bindgen` 0.69.5. Thanks Ben for the quick reply on the updates that were ongoing. Fedora 40 and earlier are OK (older `libclang`, and they also now carry `bindgen` 0.69.5). - Debian Sid was affected but is now OK, since they now ship a patched `bindgen` binary (0.66.1-7+b3). The issue was reported to Debian by email and then as a bug report [2]. Thanks NoisyCoil and Matthias for the quick replies. NoisyCoil handled the needed updates. Debian may upgrade to `bindgen` 0.70.x, too. Debian Testing is OK (older `libclang` so far). - Ubuntu non-LTS (oracular) is affected. The issue was reported to Ubuntu by email and then as a bug report [3]. Ubuntu LTS is not affected (older `libclang` so far). - Arch Linux, Gentoo Linux and openSUSE should be OK (newer `bindgen` is provided). Nix as well (older `libclang` so far). This issue was also added to our "live list" that tracks issues around distributions [4]. Cc: Ben Beasley <[email protected]> Cc: NoisyCoil <[email protected]> Cc: Matthias Geiger <[email protected]> Link: https://lore.kernel.org/rust-for-linux/[email protected]/ [1] Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1086510 [2] Link: https://bugs.launchpad.net/ubuntu/+source/rust-bindgen-cli/+bug/2086639 [3] Link: Rust-for-Linux#1127 [4] Co-developed-by: Alice Ryhl <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent d072acd commit b2603f8

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

scripts/rust_is_available.sh

+15
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
225225
exit 1
226226
fi
227227

228+
if [ "$bindgen_libclang_cversion" -ge 1900100 ] &&
229+
[ "$rust_bindings_generator_cversion" -lt 6905 ]; then
230+
# Distributions may have patched the issue (e.g. Debian did).
231+
if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_concat.h | grep -q foofoo; then
232+
echo >&2 "***"
233+
echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.69.5 together with libclang >= 19.1"
234+
echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2824),"
235+
echo >&2 "*** unless patched (like Debian's)."
236+
echo >&2 "*** Your bindgen version: $rust_bindings_generator_version"
237+
echo >&2 "*** Your libclang version: $bindgen_libclang_version"
238+
echo >&2 "***"
239+
warning=1
240+
fi
241+
fi
242+
228243
# If the C compiler is Clang, then we can also check whether its version
229244
# matches the `libclang` version used by the Rust bindings generator.
230245
#
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#define F(x) int x##x
3+
F(foo);

scripts/rust_is_available_test.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def generate_rustc(cls, stdout):
5454
""")
5555

5656
@classmethod
57-
def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False):
57+
def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
5858
if libclang_stderr is None:
5959
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
6060
else:
@@ -65,12 +65,19 @@ def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=
6565
else:
6666
version_0_66_case = "raise SystemExit(1)"
6767

68+
if libclang_concat_patched:
69+
libclang_concat_case = "print('pub static mut foofoo: ::std::os::raw::c_int;')"
70+
else:
71+
libclang_concat_case = "pass"
72+
6873
return cls.generate_executable(f"""#!/usr/bin/env python3
6974
import sys
7075
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
7176
{libclang_case}
7277
elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
7378
{version_0_66_case}
79+
elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
80+
{libclang_concat_case}
7481
else:
7582
print({repr(version_stdout)})
7683
""")
@@ -268,6 +275,31 @@ def test_bindgen_libclang_old_version(self):
268275
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
269276
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
270277

278+
def test_bindgen_bad_libclang_concat(self):
279+
for (bindgen_version, libclang_version, expected_not_patched) in (
280+
("0.69.4", "18.0.0", self.Expected.SUCCESS),
281+
("0.69.4", "19.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
282+
("0.69.4", "19.2.0", self.Expected.SUCCESS_WITH_WARNINGS),
283+
284+
("0.69.5", "18.0.0", self.Expected.SUCCESS),
285+
("0.69.5", "19.1.0", self.Expected.SUCCESS),
286+
("0.69.5", "19.2.0", self.Expected.SUCCESS),
287+
288+
("0.70.0", "18.0.0", self.Expected.SUCCESS),
289+
("0.70.0", "19.1.0", self.Expected.SUCCESS),
290+
("0.70.0", "19.2.0", self.Expected.SUCCESS),
291+
):
292+
with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
293+
cc = self.generate_clang(f"clang version {libclang_version}")
294+
libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
295+
bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
296+
result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
297+
if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
298+
self.assertIn(f"Rust bindings generator '{bindgen}' < 0.69.5 together with libclang >= 19.1", result.stderr)
299+
300+
bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
301+
result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
302+
271303
def test_clang_matches_bindgen_libclang_different_bindgen(self):
272304
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
273305
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })

0 commit comments

Comments
 (0)