From 422241773679bfd6504852442dbf970ff69a28fb Mon Sep 17 00:00:00 2001 From: Igor Velkov <325961+iav@users.noreply.github.com> Date: Tue, 10 Feb 2026 04:23:53 +0200 Subject: [PATCH] extensions: add kernel-rust extension for Rust support in kernel builds Add extension that enables CONFIG_RUST in kernel menuconfig and configures the build environment (rustc, rustfmt, bindgen, rust-src) using versioned APT packages from noble-security/noble-updates. Handles env -i in run_kernel_make_internal by passing tool paths via RUSTC, RUSTFMT, BINDGEN make params and RUST_LIB_SRC env var. Includes optional RUST_KERNEL_SAMPLES=yes for building sample Rust modules (rust_minimal, rust_print, rust_driver_faux) as a toolchain smoke test. Tested: kernel 6.19 build for rockchip64 on aarch64, both with and without Docker. Co-Authored-By: Claude Opus 4.6 --- extensions/kernel-rust.sh | 219 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 extensions/kernel-rust.sh diff --git a/extensions/kernel-rust.sh b/extensions/kernel-rust.sh new file mode 100644 index 0000000000..4c43bfff26 --- /dev/null +++ b/extensions/kernel-rust.sh @@ -0,0 +1,219 @@ +# Enable Rust support for Linux kernel compilation. +# +# Installs Rust toolchain via rustup into ${SRC}/cache/tools/rustup/ and +# configures the build environment so that CONFIG_RUST appears in kernel +# menuconfig and gets enabled automatically. +# +# The toolchain is cached by a hash of (RUST_VERSION, BINDGEN_VERSION, arch, +# RUST_EXTRA_COMPONENTS, RUST_EXTRA_CARGO_CRATES). Changing any of these +# triggers a full reinstall on the next build. +# +# Other extensions can request additional rustup components or cargo crates: +# RUST_EXTRA_COMPONENTS+=("clippy" "llvm-tools") +# RUST_EXTRA_CARGO_CRATES+=("mdbook" "cargo-deb@2.11.0") +# +# Usage: ./compile.sh kernel-config BOARD=... BRANCH=... ENABLE_EXTENSIONS="kernel-rust" +# +# References: +# https://docs.kernel.org/rust/quick-start.html +# https://docs.kernel.org/rust/general-information.html +# https://rust-for-linux.com/rust-version-policy +# https://rust-lang.github.io/rustup/installation/index.html + +# Rust toolchain version installed via rustup. +# Kernel >= 6.12 requires rustc >= 1.78. See rust-version-policy above. +RUST_VERSION="${RUST_VERSION:-1.85.0}" + +# bindgen-cli version installed via cargo. +# APT bindgen 0.66.1 panics on kernel >= 6.19 headers (FromBytesWithNulError +# in codegen/mod.rs). Fixed in >= 0.69. +BINDGEN_VERSION="${BINDGEN_VERSION:-0.71.1}" + +# Enable Rust sample kernel modules for toolchain smoke testing. +# Set to "yes" to build rust_minimal, rust_print, rust_driver_faux as modules. +# Can also be set via command line: RUST_KERNEL_SAMPLES=yes +RUST_KERNEL_SAMPLES="${RUST_KERNEL_SAMPLES:-no}" + +# Extra rustup components to install (e.g. clippy, llvm-tools). +# Other extensions can append: RUST_EXTRA_COMPONENTS+=("clippy") +declare -g -a RUST_EXTRA_COMPONENTS=() + +# Extra cargo crates to install. Supports "name" or "name@version" syntax. +# Other extensions can append: RUST_EXTRA_CARGO_CRATES+=("mdbook" "cargo-deb@2.11.0") +declare -g -a RUST_EXTRA_CARGO_CRATES=() + +# Resolved tool paths, set by host_dependencies_ready, used by custom_kernel_make_params. +declare -g RUST_TOOL_RUSTC="" +declare -g RUST_TOOL_RUSTFMT="" +declare -g RUST_TOOL_BINDGEN="" +declare -g RUST_TOOL_SYSROOT="" + +function add_host_dependencies__add_rust_compiler() { + display_alert "Adding Rust kernel build dependencies" "${EXTENSION}" "info" + # bindgen needs libclang for dlopen; available on all target distros. + EXTRA_BUILD_DEPS+=" libclang-dev " +} + +# Download rustup-init binary for the current architecture. +# Follows the project pattern: curl → .tmp → mv → chmod. +_download_rustup_init() { + local target_dir="$1" + local target_triple + case "${BASH_VERSINFO[5]}" in + *aarch64*) target_triple="aarch64-unknown-linux-gnu" ;; + *x86_64*) target_triple="x86_64-unknown-linux-gnu" ;; + *riscv64*) target_triple="riscv64gc-unknown-linux-gnu" ;; + *) exit_with_error "Unsupported architecture for rustup" "${BASH_VERSINFO[5]}" ;; + esac + + local url="https://static.rust-lang.org/rustup/dist/${target_triple}/rustup-init" + local dest="${target_dir}/rustup-init" + + display_alert "Downloading rustup-init" "${target_triple}" "info" + curl --proto '=https' --tlsv1.2 -sSf -o "${dest}.tmp" "${url}" + mv "${dest}.tmp" "${dest}" + chmod +x "${dest}" +} + +# Install or reuse cached Rust toolchain in ${SRC}/cache/tools/rustup/. +_prepare_rust_toolchain() { + local rust_cache_dir="${SRC}/cache/tools/rustup" + mkdir -p "${rust_cache_dir}" + + local rustup_home="${rust_cache_dir}/rustup-home" + local cargo_home="${rust_cache_dir}/cargo-home" + + # Content-addressable cache: hash of version config + architecture + extras + local cache_key="${RUST_VERSION}|${BINDGEN_VERSION}|${BASH_VERSINFO[5]}" + cache_key+="|${RUST_EXTRA_COMPONENTS[*]}|${RUST_EXTRA_CARGO_CRATES[*]}" + local cache_hash + cache_hash="$(echo -n "${cache_key}" | sha256sum | cut -c1-16)" + local marker="${rust_cache_dir}/.marker-${cache_hash}" + + if [[ -f "${marker}" ]]; then + display_alert "Rust toolchain cache hit" "${cache_hash}" "cachehit" + return 0 + fi + + # Remove stale markers from previous versions + rm -f "${rust_cache_dir}"/.marker-* + + display_alert "Installing Rust toolchain" "rustc ${RUST_VERSION}, bindgen ${BINDGEN_VERSION}" "info" + + # Download rustup-init + do_with_retries 3 _download_rustup_init "${rust_cache_dir}" + + # Install minimal toolchain; SKIP_PATH_CHECK suppresses warnings about + # system rustc in /usr/bin (e.g. from mtkflash in Docker images). + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ + RUSTUP_INIT_SKIP_PATH_CHECK=yes \ + "${rust_cache_dir}/rustup-init" -y \ + --profile minimal \ + --default-toolchain "${RUST_VERSION}" \ + --no-modify-path + + # Components: rustfmt (not in minimal profile) + rust-src (kernel needs it) + extras + local -a components=(rustfmt rust-src "${RUST_EXTRA_COMPONENTS[@]}") + display_alert "Installing rustup components" "${components[*]}" "info" + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ + "${cargo_home}/bin/rustup" component add "${components[@]}" + + # Cargo crates: bindgen-cli (kernel needs it) + extras + # Supports "name" or "name@version" syntax. + local -a crates=("bindgen-cli@${BINDGEN_VERSION}" "${RUST_EXTRA_CARGO_CRATES[@]}") + local crate + for crate in "${crates[@]}"; do + display_alert "Installing cargo crate" "${crate}" "info" + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ + "${cargo_home}/bin/cargo" install --locked "${crate}" + done + + # Mark cache as valid only after everything succeeds + touch "${marker}" + display_alert "Rust toolchain installed" "${cache_hash}" "info" +} + +# Resolve absolute paths to Rust tool binaries. +# Uses direct paths into the toolchain (not rustup proxies), so that +# env -i in run_kernel_make_internal() does not need RUSTUP_HOME set. +_resolve_rust_tool_paths() { + local rustup_home="${SRC}/cache/tools/rustup/rustup-home" + local cargo_home="${SRC}/cache/tools/rustup/cargo-home" + + RUST_TOOL_SYSROOT="$(RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ + "${cargo_home}/bin/rustc" --print sysroot)" + + # Direct binaries inside the toolchain, bypassing rustup proxy + RUST_TOOL_RUSTC="${RUST_TOOL_SYSROOT}/bin/rustc" + RUST_TOOL_RUSTFMT="${RUST_TOOL_SYSROOT}/bin/rustfmt" + RUST_TOOL_BINDGEN="${cargo_home}/bin/bindgen" +} + +function host_dependencies_ready__add_rust_compiler() { + _prepare_rust_toolchain + _resolve_rust_tool_paths + + # Verify all tools are executable + local tool_name tool_path + for tool_name in RUST_TOOL_RUSTC RUST_TOOL_RUSTFMT RUST_TOOL_BINDGEN; do + tool_path="${!tool_name}" + [[ -x "${tool_path}" ]] || exit_with_error "Required Rust tool '${tool_name}' not found at ${tool_path}" "${EXTENSION}" + done + + display_alert "Rust toolchain ready" \ + "rustc $(${RUST_TOOL_RUSTC} --version | awk '{print $2}'), bindgen $(${RUST_TOOL_BINDGEN} --version 2>&1 | awk '{print $2}')" "info" +} + +function artifact_kernel_version_parts__add_rust_version() { + # Include Rust toolchain version in artifact hash so that changing + # RUST_VERSION or BINDGEN_VERSION triggers a kernel rebuild. + local cache_key="${RUST_VERSION}|${BINDGEN_VERSION}" + local short + short="$(echo -n "${cache_key}" | sha256sum | cut -c1-4)" + + artifact_version_parts["_R"]="rust${short}" + + # Add to order array if not already present + local found=0 entry + for entry in "${artifact_version_part_order[@]}"; do + [[ "${entry}" == *"-_R" ]] && found=1 && break + done + if [[ "${found}" -eq 0 ]]; then + artifact_version_part_order+=("0086-_R") + fi +} + +function custom_kernel_config__add_rust_compiler() { + # https://docs.kernel.org/rust/quick-start.html + opts_y+=("RUST") + + # Build sample Rust modules for toolchain smoke testing + if [[ "${RUST_KERNEL_SAMPLES}" == "yes" ]]; then + display_alert "Enabling Rust sample modules" "${EXTENSION}" "info" + opts_y+=("SAMPLES") # Parent menu for all kernel samples + opts_y+=("SAMPLES_RUST") + opts_m+=("SAMPLE_RUST_MINIMAL") + opts_m+=("SAMPLE_RUST_PRINT") + opts_m+=("SAMPLE_RUST_DRIVER_FAUX") + fi +} + +function custom_kernel_make_params__add_rust_compiler() { + # run_kernel_make_internal uses "env -i" which clears all environment + # variables, so we pass Rust paths explicitly via make parameters. + # Using direct toolchain binaries (not rustup proxies) avoids needing + # RUSTUP_HOME in the env -i context. + + common_make_params_quoted+=("RUSTC=${RUST_TOOL_RUSTC}") + common_make_params_quoted+=("RUSTFMT=${RUST_TOOL_RUSTFMT}") + common_make_params_quoted+=("BINDGEN=${RUST_TOOL_BINDGEN}") + + # Rust standard library source path for kernel build + local rust_lib_src="${RUST_TOOL_SYSROOT}/lib/rustlib/src/rust/library" + if [[ -d "${rust_lib_src}" ]]; then + display_alert "Rust library source" "${rust_lib_src}" "info" + common_make_envs+=("RUST_LIB_SRC='${rust_lib_src}'") + else + display_alert "Rust library source not found" "CONFIG_RUST will not appear in menuconfig" "wrn" + fi +}