Skip to content

Commit fa00690

Browse files
iavclaude
andcommitted
kernel-rust: match LLVM/clang version to bindgen's libclang
bindgen-0.71 links against libclang-20, but the extension was installing unversioned clang (18 on noble). This caused: - kernel "libclang version does not match Clang's" warning - CC=ccache clang (18) overriding LLVM=-20 on make command line Changes: - Auto-detect LLVM version from bindgen's dependency chain, resolving versioned (bindgen-0.71 → libclang-20-dev) and unversioned (bindgen → libclang-dev → libclang-NN-dev) paths. Tested on Ubuntu 24.04, Debian Trixie, Ubuntu 25.10, 26.04. - Install clang-VER/lld-VER/llvm-VER instead of unversioned. - Replace LLVM=1 → LLVM=-VER and CC=ccache clang → CC=ccache clang-VER in make params so the entire toolchain is consistent. - Use _apt_pick() to prefer versioned packages (rustc-1.85) on noble, falling back to unversioned (rustc) on Debian/newer Ubuntu. - Override artifact hash _T key with actual clang version, without modifying kernel-version-toolchain extension. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c486fbe commit fa00690

File tree

1 file changed

+135
-28
lines changed

1 file changed

+135
-28
lines changed

extensions/kernel-rust.sh

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,42 @@ RUST_APT_VERSION="1.85"
2626
# https://launchpad.net/ubuntu/noble/arm64/bindgen-0.71
2727
BINDGEN_APT_VERSION="0.71"
2828

29+
# LLVM/Clang version must match the libclang that bindgen links against.
30+
# Auto-detect by resolving the dependency chain:
31+
# bindgen-0.71 → libclang-20-dev (versioned, Ubuntu 24.04)
32+
# bindgen → libclang-dev → libclang-NN-dev (unversioned, Debian/newer Ubuntu)
33+
# Using mismatched versions (e.g. clang-18 + libclang-20) triggers a kernel
34+
# warning at "make rustavailable" time.
35+
_detect_llvm_version() {
36+
local bindgen_pkg ver candidate
37+
# Try versioned bindgen first (Ubuntu 24.04), then unversioned.
38+
# Only query real (installable) packages — virtual packages like
39+
# bindgen-0.71 on Trixie return provider lists that contain
40+
# "testing-only-libclang-16" strings, producing false matches.
41+
for bindgen_pkg in "bindgen-${BINDGEN_APT_VERSION}" "bindgen"; do
42+
candidate="$(apt-cache policy "${bindgen_pkg}" 2> /dev/null \
43+
| sed -n 's/.*Candidate: *//p')"
44+
[[ -n "${candidate}" && "${candidate}" != "(none)" ]] || continue
45+
# Parse only "Depends:" lines, not virtual package providers
46+
ver="$(apt-cache depends "${bindgen_pkg}" 2> /dev/null \
47+
| sed -n 's/^[[:space:]]*Depends:.*libclang-\([0-9]\+\)-dev.*/\1/p' | head -1)"
48+
if [[ -n "${ver}" ]]; then
49+
echo "${ver}"
50+
return
51+
fi
52+
done
53+
# Unversioned libclang-dev → resolve to libclang-NN-dev
54+
ver="$(apt-cache depends libclang-dev 2> /dev/null \
55+
| sed -n 's/^[[:space:]]*Depends:.*libclang-\([0-9]\+\)-dev.*/\1/p' | head -1)"
56+
if [[ -n "${ver}" ]]; then
57+
echo "${ver}"
58+
return
59+
fi
60+
}
61+
LLVM_APT_VERSION="$(_detect_llvm_version)" || true
62+
LLVM_APT_VERSION="${LLVM_APT_VERSION:-20}" # fallback if apt-cache unavailable
63+
unset -f _detect_llvm_version
64+
2965
# Enable Rust sample kernel modules for toolchain smoke testing.
3066
# Set to "yes" to build rust_minimal, rust_print, rust_driver_faux as modules.
3167
# Can also be set via command line: RUST_KERNEL_SAMPLES=yes
@@ -39,18 +75,34 @@ declare -g RUST_TOOL_BINDGEN=""
3975
function add_host_dependencies__add_rust_compiler() {
4076
display_alert "Adding Rust kernel build dependencies" "${EXTENSION}" "info"
4177

42-
# --- Method 1: APT versioned packages (noble-security/noble-updates) ---
43-
# Versioned packages install binaries under /usr/lib/rust-X.YY/bin/ or
44-
# /usr/bin/tool-X.YY; we locate them via _find_rust_tool and pass
45-
# explicit paths to make via custom_kernel_make_params.
46-
EXTRA_BUILD_DEPS+=" rustc-${RUST_APT_VERSION} cargo-${RUST_APT_VERSION} "
47-
EXTRA_BUILD_DEPS+=" rust-${RUST_APT_VERSION}-src rustfmt-${RUST_APT_VERSION} "
48-
EXTRA_BUILD_DEPS+=" bindgen-${BINDGEN_APT_VERSION} "
49-
EXTRA_BUILD_DEPS+=" libclang-dev clang lld llvm "
78+
# Rust packages: try versioned first (Ubuntu 24.04), fall back to unversioned
79+
# (Debian trixie, Ubuntu >= 25.10). _apt_pick outputs the first available.
80+
local pkg
81+
for pkg in rustc cargo rustfmt; do
82+
EXTRA_BUILD_DEPS+=" $(_apt_pick "${pkg}-${RUST_APT_VERSION}" "${pkg}") "
83+
done
84+
EXTRA_BUILD_DEPS+=" $(_apt_pick "rust-${RUST_APT_VERSION}-src" rust-src) "
85+
EXTRA_BUILD_DEPS+=" $(_apt_pick "bindgen-${BINDGEN_APT_VERSION}" bindgen) "
5086

51-
# --- Method 2: Rustup (commented out) ---
52-
# Only libclang/llvm needed; rustc/cargo/bindgen come from rustup/cargo.
53-
#EXTRA_BUILD_DEPS+=" libclang-dev clang lld llvm "
87+
# LLVM toolchain: versioned to match libclang used by bindgen
88+
EXTRA_BUILD_DEPS+=" clang-${LLVM_APT_VERSION} lld-${LLVM_APT_VERSION} llvm-${LLVM_APT_VERSION} "
89+
}
90+
91+
# Pick first installable APT package from candidates.
92+
# Uses apt-cache policy to distinguish real packages from virtual ones
93+
# (apt-cache show returns 0 for virtual packages like bindgen-0.71 on Trixie).
94+
_apt_pick() {
95+
local pkg candidate
96+
for pkg in "$@"; do
97+
candidate="$(apt-cache policy "${pkg}" 2> /dev/null \
98+
| sed -n 's/.*Candidate: *//p' | head -1)"
99+
if [[ -n "${candidate}" && "${candidate}" != "(none)" ]]; then
100+
echo "${pkg}"
101+
return
102+
fi
103+
done
104+
# None found — return first candidate and let apt fail with a clear error
105+
echo "$1"
54106
}
55107

56108
# Find a versioned tool binary, returning its full path.
@@ -62,17 +114,15 @@ _find_rust_tool() {
62114
local tool_path=""
63115
# 1. Try versioned command in PATH (e.g. rustc-1.85)
64116
if [[ -n "${version}" ]]; then
65-
tool_path="$(command -v "${base}-${version}" 2> /dev/null || true)"
117+
local versioned="${base}-${version}"
118+
tool_path="$(command -v "${versioned}" 2> /dev/null || true)"
66119
if [[ -n "${tool_path}" ]]; then
67120
echo "${tool_path}"
68121
return
69122
fi
70-
fi
71-
# 2. Locate binary via dpkg package file list
72-
if [[ -n "${version}" ]]; then
73-
local pkg_name="${base}-${version}"
74-
if dpkg -s "${pkg_name}" > /dev/null 2>&1; then
75-
tool_path="$(dpkg -L "${pkg_name}" 2> /dev/null | grep "/bin/${base}" | head -1 || true)"
123+
# 2. Locate binary via dpkg package file list
124+
if dpkg -s "${versioned}" > /dev/null 2>&1; then
125+
tool_path="$(dpkg -L "${versioned}" 2> /dev/null | grep "/bin/${base}" | head -1 || true)"
76126
if [[ -n "${tool_path}" && -x "${tool_path}" ]]; then
77127
display_alert "Found ${base} via dpkg" "${tool_path}" "info"
78128
echo "${tool_path}"
@@ -97,16 +147,7 @@ function host_dependencies_ready__add_rust_compiler() {
97147
local tool_name tool_path
98148
for tool_name in RUST_TOOL_RUSTC RUST_TOOL_RUSTFMT RUST_TOOL_BINDGEN; do
99149
tool_path="${!tool_name}"
100-
if [[ -z "${tool_path}" ]]; then
101-
display_alert "PATH" "${PATH}" "wrn"
102-
display_alert "dpkg -L rustfmt-${RUST_APT_VERSION}" \
103-
"$(dpkg -L "rustfmt-${RUST_APT_VERSION}" 2>&1 | grep bin || echo 'N/A')" "wrn"
104-
display_alert "dpkg -L rustc-${RUST_APT_VERSION}" \
105-
"$(dpkg -L "rustc-${RUST_APT_VERSION}" 2>&1 | grep bin || echo 'N/A')" "wrn"
106-
display_alert "dpkg -L bindgen-${BINDGEN_APT_VERSION}" \
107-
"$(dpkg -L "bindgen-${BINDGEN_APT_VERSION}" 2>&1 | grep bin || echo 'N/A')" "wrn"
108-
exit_with_error "Required Rust tool '${tool_name}' not found" "${EXTENSION}"
109-
fi
150+
[[ -n "${tool_path}" ]] || _missing_rust_tool_abort "${tool_name}"
110151
done
111152

112153
display_alert "Rust toolchain ready" \
@@ -154,13 +195,55 @@ function host_dependencies_ready__add_rust_compiler() {
154195
# "rustc $(rustc --version | awk '{print $2}'), bindgen $(bindgen --version 2>&1 | awk '{print $2}')" "info"
155196
}
156197

198+
_show_dpkg_bins() {
199+
local pkg
200+
for pkg in "$@"; do
201+
display_alert "dpkg -L ${pkg}" \
202+
"$(dpkg -L "${pkg}" 2>&1 | grep bin || echo 'N/A')" "wrn"
203+
done
204+
}
205+
206+
_missing_rust_tool_abort() {
207+
local tool_name="$1"
208+
display_alert "PATH" "${PATH}" "wrn"
209+
_show_dpkg_bins "rustfmt-${RUST_APT_VERSION}" "rustc-${RUST_APT_VERSION}" "bindgen-${BINDGEN_APT_VERSION}"
210+
exit_with_error "Required Rust tool '${tool_name}' not found" "${EXTENSION}"
211+
}
212+
213+
# Override the compiler version in artifact hash when using versioned clang.
214+
# kernel-version-toolchain (if enabled) sets _T from unversioned "clang" in PATH,
215+
# but this extension redirects the build to clang-VER via LLVM=-VER.
216+
# Runs after add_toolchain (alphabetically: "override" > "add").
217+
function artifact_kernel_version_parts__override_toolchain_version() {
218+
[[ "${KERNEL_COMPILER}" == "clang" && -n "${LLVM_APT_VERSION}" ]] || return 0
219+
local clang_bin="clang-${LLVM_APT_VERSION}"
220+
command -v "${clang_bin}" &> /dev/null || return 0
221+
222+
local full_version short_version
223+
full_version="$("${clang_bin}" -dumpfullversion -dumpversion 2> /dev/null || echo "")"
224+
[[ -n "${full_version}" ]] || return 0
225+
short_version="$(echo "${full_version}" | cut -d'.' -f1-2)"
226+
227+
artifact_version_parts["_T"]="clang${short_version}"
228+
# Ensure the key is in the order array (kernel-version-toolchain may have added it,
229+
# but if that extension is not enabled, we need to add it ourselves)
230+
local found=0 entry
231+
for entry in "${artifact_version_part_order[@]}"; do
232+
[[ "${entry}" == *"-_T" ]] && found=1 && break
233+
done
234+
if [[ "${found}" -eq 0 ]]; then
235+
artifact_version_part_order+=("0085-_T")
236+
fi
237+
}
238+
157239
function custom_kernel_config__add_rust_compiler() {
158240
# https://docs.kernel.org/rust/quick-start.html
159241
opts_y+=("RUST")
160242

161243
# Build sample Rust modules for toolchain smoke testing
162244
if [[ "${RUST_KERNEL_SAMPLES}" == "yes" ]]; then
163245
display_alert "Enabling Rust sample modules" "${EXTENSION}" "info"
246+
opts_y+=("SAMPLES") # Parent menu for all kernel samples
164247
opts_y+=("SAMPLES_RUST")
165248
opts_m+=("SAMPLE_RUST_MINIMAL")
166249
opts_m+=("SAMPLE_RUST_PRINT")
@@ -172,6 +255,30 @@ function custom_kernel_make_params__add_rust_compiler() {
172255
# run_kernel_make_internal uses "env -i" which clears all environment
173256
# variables, so we must pass Rust paths explicitly.
174257

258+
# When building with clang, replace LLVM=1 with LLVM=-VER so that the
259+
# kernel uses versioned LLVM tools (clang-20, ld.lld-20, llvm-ar-20, …)
260+
# matching the libclang version that bindgen links against.
261+
# Also update CC= to use versioned clang, because kernel-make.sh sets
262+
# CC=ccache clang (unversioned) which overrides the CC that LLVM=-VER
263+
# would derive in the kernel Makefile.
264+
# See: https://docs.kernel.org/kbuild/llvm.html#llvm-utility
265+
if [[ "${KERNEL_COMPILER}" == "clang" && -n "${LLVM_APT_VERSION}" ]]; then
266+
local i
267+
for i in "${!common_make_params_quoted[@]}"; do
268+
case "${common_make_params_quoted[${i}]}" in
269+
LLVM=1)
270+
common_make_params_quoted[${i}]="LLVM=-${LLVM_APT_VERSION}"
271+
display_alert "Using versioned LLVM toolchain" "LLVM=-${LLVM_APT_VERSION}" "info"
272+
;;
273+
CC=*clang)
274+
# Replace "CC=ccache clang" → "CC=ccache clang-20"
275+
common_make_params_quoted[${i}]="${common_make_params_quoted[${i}]/%clang/clang-${LLVM_APT_VERSION}}"
276+
display_alert "Using versioned clang" "clang-${LLVM_APT_VERSION}" "info"
277+
;;
278+
esac
279+
done
280+
fi
281+
175282
# --- Method 1: APT versioned packages ---
176283
# Tell the kernel build system to use the discovered tool names.
177284
if [[ -n "${RUST_TOOL_RUSTC}" ]]; then

0 commit comments

Comments
 (0)