|
| 1 | +# Enable Rust support for Linux kernel compilation. |
| 2 | +# |
| 3 | +# Installs Rust toolchain via rustup into ${SRC}/cache/tools/rustup/ and |
| 4 | +# configures the build environment so that CONFIG_RUST appears in kernel |
| 5 | +# menuconfig and gets enabled automatically. |
| 6 | +# |
| 7 | +# The toolchain is cached by a hash of (RUST_VERSION, BINDGEN_VERSION, arch, |
| 8 | +# RUST_EXTRA_COMPONENTS, RUST_EXTRA_CARGO_CRATES). Changing any of these |
| 9 | +# triggers a full reinstall on the next build. |
| 10 | +# |
| 11 | +# Other extensions can request additional rustup components or cargo crates: |
| 12 | +# RUST_EXTRA_COMPONENTS+=("clippy" "llvm-tools") |
| 13 | +# RUST_EXTRA_CARGO_CRATES+=("mdbook" "cargo-deb@2.11.0") |
| 14 | +# |
| 15 | +# Usage: ./compile.sh kernel-config BOARD=... BRANCH=... ENABLE_EXTENSIONS="kernel-rust" |
| 16 | +# |
| 17 | +# References: |
| 18 | +# https://docs.kernel.org/rust/quick-start.html |
| 19 | +# https://docs.kernel.org/rust/general-information.html |
| 20 | +# https://rust-for-linux.com/rust-version-policy |
| 21 | +# https://rust-lang.github.io/rustup/installation/index.html |
| 22 | + |
| 23 | +# Rust toolchain version installed via rustup. |
| 24 | +# Kernel >= 6.12 requires rustc >= 1.78. See rust-version-policy above. |
| 25 | +RUST_VERSION="${RUST_VERSION:-1.85.0}" |
| 26 | + |
| 27 | +# bindgen-cli version installed via cargo. |
| 28 | +# APT bindgen 0.66.1 panics on kernel >= 6.19 headers (FromBytesWithNulError |
| 29 | +# in codegen/mod.rs). Fixed in >= 0.69. |
| 30 | +BINDGEN_VERSION="${BINDGEN_VERSION:-0.71.1}" |
| 31 | + |
| 32 | +# Enable Rust sample kernel modules for toolchain smoke testing. |
| 33 | +# Set to "yes" to build rust_minimal, rust_print, rust_driver_faux as modules. |
| 34 | +# Can also be set via command line: RUST_KERNEL_SAMPLES=yes |
| 35 | +RUST_KERNEL_SAMPLES="${RUST_KERNEL_SAMPLES:-no}" |
| 36 | + |
| 37 | +# Extra rustup components to install (e.g. clippy, llvm-tools). |
| 38 | +# Other extensions can append: RUST_EXTRA_COMPONENTS+=("clippy") |
| 39 | +declare -g -a RUST_EXTRA_COMPONENTS=() |
| 40 | + |
| 41 | +# Extra cargo crates to install. Supports "name" or "name@version" syntax. |
| 42 | +# Other extensions can append: RUST_EXTRA_CARGO_CRATES+=("mdbook" "cargo-deb@2.11.0") |
| 43 | +declare -g -a RUST_EXTRA_CARGO_CRATES=() |
| 44 | + |
| 45 | +# Resolved tool paths, set by host_dependencies_ready, used by custom_kernel_make_params. |
| 46 | +declare -g RUST_TOOL_RUSTC="" |
| 47 | +declare -g RUST_TOOL_RUSTFMT="" |
| 48 | +declare -g RUST_TOOL_BINDGEN="" |
| 49 | +declare -g RUST_TOOL_SYSROOT="" |
| 50 | + |
| 51 | +function add_host_dependencies__add_rust_compiler() { |
| 52 | + display_alert "Adding Rust kernel build dependencies" "${EXTENSION}" "info" |
| 53 | + # bindgen needs libclang for dlopen; available on all target distros. |
| 54 | + EXTRA_BUILD_DEPS+=" libclang-dev " |
| 55 | +} |
| 56 | + |
| 57 | +# Download rustup-init binary for the current architecture. |
| 58 | +# Follows the project pattern: curl → .tmp → mv → chmod. |
| 59 | +_download_rustup_init() { |
| 60 | + local target_dir="$1" |
| 61 | + local target_triple |
| 62 | + case "${BASH_VERSINFO[5]}" in |
| 63 | + *aarch64*) target_triple="aarch64-unknown-linux-gnu" ;; |
| 64 | + *x86_64*) target_triple="x86_64-unknown-linux-gnu" ;; |
| 65 | + *riscv64*) target_triple="riscv64gc-unknown-linux-gnu" ;; |
| 66 | + *) exit_with_error "Unsupported architecture for rustup" "${BASH_VERSINFO[5]}" ;; |
| 67 | + esac |
| 68 | + |
| 69 | + local url="https://static.rust-lang.org/rustup/dist/${target_triple}/rustup-init" |
| 70 | + local dest="${target_dir}/rustup-init" |
| 71 | + |
| 72 | + display_alert "Downloading rustup-init" "${target_triple}" "info" |
| 73 | + curl --proto '=https' --tlsv1.2 -sSf -o "${dest}.tmp" "${url}" |
| 74 | + mv "${dest}.tmp" "${dest}" |
| 75 | + chmod +x "${dest}" |
| 76 | +} |
| 77 | + |
| 78 | +# Install or reuse cached Rust toolchain in ${SRC}/cache/tools/rustup/. |
| 79 | +_prepare_rust_toolchain() { |
| 80 | + local rust_cache_dir="${SRC}/cache/tools/rustup" |
| 81 | + mkdir -p "${rust_cache_dir}" |
| 82 | + |
| 83 | + local rustup_home="${rust_cache_dir}/rustup-home" |
| 84 | + local cargo_home="${rust_cache_dir}/cargo-home" |
| 85 | + |
| 86 | + # Content-addressable cache: hash of version config + architecture + extras |
| 87 | + local cache_key="${RUST_VERSION}|${BINDGEN_VERSION}|${BASH_VERSINFO[5]}" |
| 88 | + cache_key+="|${RUST_EXTRA_COMPONENTS[*]}|${RUST_EXTRA_CARGO_CRATES[*]}" |
| 89 | + local cache_hash |
| 90 | + cache_hash="$(echo -n "${cache_key}" | sha256sum | cut -c1-16)" |
| 91 | + local marker="${rust_cache_dir}/.marker-${cache_hash}" |
| 92 | + |
| 93 | + if [[ -f "${marker}" ]]; then |
| 94 | + display_alert "Rust toolchain cache hit" "${cache_hash}" "cachehit" |
| 95 | + return 0 |
| 96 | + fi |
| 97 | + |
| 98 | + # Remove stale markers from previous versions |
| 99 | + rm -f "${rust_cache_dir}"/.marker-* |
| 100 | + |
| 101 | + display_alert "Installing Rust toolchain" "rustc ${RUST_VERSION}, bindgen ${BINDGEN_VERSION}" "info" |
| 102 | + |
| 103 | + # Download rustup-init |
| 104 | + do_with_retries 3 _download_rustup_init "${rust_cache_dir}" |
| 105 | + |
| 106 | + # Install minimal toolchain; SKIP_PATH_CHECK suppresses warnings about |
| 107 | + # system rustc in /usr/bin (e.g. from mtkflash in Docker images). |
| 108 | + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ |
| 109 | + RUSTUP_INIT_SKIP_PATH_CHECK=yes \ |
| 110 | + "${rust_cache_dir}/rustup-init" -y \ |
| 111 | + --profile minimal \ |
| 112 | + --default-toolchain "${RUST_VERSION}" \ |
| 113 | + --no-modify-path |
| 114 | + |
| 115 | + # Components: rustfmt (not in minimal profile) + rust-src (kernel needs it) + extras |
| 116 | + local -a components=(rustfmt rust-src "${RUST_EXTRA_COMPONENTS[@]}") |
| 117 | + display_alert "Installing rustup components" "${components[*]}" "info" |
| 118 | + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ |
| 119 | + "${cargo_home}/bin/rustup" component add "${components[@]}" |
| 120 | + |
| 121 | + # Cargo crates: bindgen-cli (kernel needs it) + extras |
| 122 | + # Supports "name" or "name@version" syntax. |
| 123 | + local -a crates=("bindgen-cli@${BINDGEN_VERSION}" "${RUST_EXTRA_CARGO_CRATES[@]}") |
| 124 | + local crate |
| 125 | + for crate in "${crates[@]}"; do |
| 126 | + display_alert "Installing cargo crate" "${crate}" "info" |
| 127 | + RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ |
| 128 | + "${cargo_home}/bin/cargo" install --locked "${crate}" |
| 129 | + done |
| 130 | + |
| 131 | + # Mark cache as valid only after everything succeeds |
| 132 | + touch "${marker}" |
| 133 | + display_alert "Rust toolchain installed" "${cache_hash}" "info" |
| 134 | +} |
| 135 | + |
| 136 | +# Resolve absolute paths to Rust tool binaries. |
| 137 | +# Uses direct paths into the toolchain (not rustup proxies), so that |
| 138 | +# env -i in run_kernel_make_internal() does not need RUSTUP_HOME set. |
| 139 | +_resolve_rust_tool_paths() { |
| 140 | + local rustup_home="${SRC}/cache/tools/rustup/rustup-home" |
| 141 | + local cargo_home="${SRC}/cache/tools/rustup/cargo-home" |
| 142 | + |
| 143 | + RUST_TOOL_SYSROOT="$(RUSTUP_HOME="${rustup_home}" CARGO_HOME="${cargo_home}" \ |
| 144 | + "${cargo_home}/bin/rustc" --print sysroot)" |
| 145 | + |
| 146 | + # Direct binaries inside the toolchain, bypassing rustup proxy |
| 147 | + RUST_TOOL_RUSTC="${RUST_TOOL_SYSROOT}/bin/rustc" |
| 148 | + RUST_TOOL_RUSTFMT="${RUST_TOOL_SYSROOT}/bin/rustfmt" |
| 149 | + RUST_TOOL_BINDGEN="${cargo_home}/bin/bindgen" |
| 150 | +} |
| 151 | + |
| 152 | +function host_dependencies_ready__add_rust_compiler() { |
| 153 | + _prepare_rust_toolchain |
| 154 | + _resolve_rust_tool_paths |
| 155 | + |
| 156 | + # Verify all tools are executable |
| 157 | + local tool_name tool_path |
| 158 | + for tool_name in RUST_TOOL_RUSTC RUST_TOOL_RUSTFMT RUST_TOOL_BINDGEN; do |
| 159 | + tool_path="${!tool_name}" |
| 160 | + [[ -x "${tool_path}" ]] || exit_with_error "Required Rust tool '${tool_name}' not found at ${tool_path}" "${EXTENSION}" |
| 161 | + done |
| 162 | + |
| 163 | + display_alert "Rust toolchain ready" \ |
| 164 | + "rustc $(${RUST_TOOL_RUSTC} --version | awk '{print $2}'), bindgen $(${RUST_TOOL_BINDGEN} --version 2>&1 | awk '{print $2}')" "info" |
| 165 | +} |
| 166 | + |
| 167 | +function artifact_kernel_version_parts__add_rust_version() { |
| 168 | + # Include Rust toolchain version in artifact hash so that changing |
| 169 | + # RUST_VERSION or BINDGEN_VERSION triggers a kernel rebuild. |
| 170 | + local cache_key="${RUST_VERSION}|${BINDGEN_VERSION}" |
| 171 | + local short |
| 172 | + short="$(echo -n "${cache_key}" | sha256sum | cut -c1-4)" |
| 173 | + |
| 174 | + artifact_version_parts["_R"]="rust${short}" |
| 175 | + |
| 176 | + # Add to order array if not already present |
| 177 | + local found=0 entry |
| 178 | + for entry in "${artifact_version_part_order[@]}"; do |
| 179 | + [[ "${entry}" == *"-_R" ]] && found=1 && break |
| 180 | + done |
| 181 | + if [[ "${found}" -eq 0 ]]; then |
| 182 | + artifact_version_part_order+=("0086-_R") |
| 183 | + fi |
| 184 | +} |
| 185 | + |
| 186 | +function custom_kernel_config__add_rust_compiler() { |
| 187 | + # https://docs.kernel.org/rust/quick-start.html |
| 188 | + opts_y+=("RUST") |
| 189 | + |
| 190 | + # Build sample Rust modules for toolchain smoke testing |
| 191 | + if [[ "${RUST_KERNEL_SAMPLES}" == "yes" ]]; then |
| 192 | + display_alert "Enabling Rust sample modules" "${EXTENSION}" "info" |
| 193 | + opts_y+=("SAMPLES") # Parent menu for all kernel samples |
| 194 | + opts_y+=("SAMPLES_RUST") |
| 195 | + opts_m+=("SAMPLE_RUST_MINIMAL") |
| 196 | + opts_m+=("SAMPLE_RUST_PRINT") |
| 197 | + opts_m+=("SAMPLE_RUST_DRIVER_FAUX") |
| 198 | + fi |
| 199 | +} |
| 200 | + |
| 201 | +function custom_kernel_make_params__add_rust_compiler() { |
| 202 | + # run_kernel_make_internal uses "env -i" which clears all environment |
| 203 | + # variables, so we pass Rust paths explicitly via make parameters. |
| 204 | + # Using direct toolchain binaries (not rustup proxies) avoids needing |
| 205 | + # RUSTUP_HOME in the env -i context. |
| 206 | + |
| 207 | + common_make_params_quoted+=("RUSTC=${RUST_TOOL_RUSTC}") |
| 208 | + common_make_params_quoted+=("RUSTFMT=${RUST_TOOL_RUSTFMT}") |
| 209 | + common_make_params_quoted+=("BINDGEN=${RUST_TOOL_BINDGEN}") |
| 210 | + |
| 211 | + # Rust standard library source path for kernel build |
| 212 | + local rust_lib_src="${RUST_TOOL_SYSROOT}/lib/rustlib/src/rust/library" |
| 213 | + if [[ -d "${rust_lib_src}" ]]; then |
| 214 | + display_alert "Rust library source" "${rust_lib_src}" "info" |
| 215 | + common_make_envs+=("RUST_LIB_SRC='${rust_lib_src}'") |
| 216 | + else |
| 217 | + display_alert "Rust library source not found" "CONFIG_RUST will not appear in menuconfig" "wrn" |
| 218 | + fi |
| 219 | +} |
0 commit comments