diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst index c4a5b87437dd68..9ecb171f711af7 100644 --- a/Documentation/rust/quick-start.rst +++ b/Documentation/rust/quick-start.rst @@ -13,7 +13,17 @@ This section explains how to fetch the tools needed for building. Some of these requirements might be available from Linux distributions under names like ``rustc``, ``rust-src``, ``rust-bindgen``, etc. However, -at the time of writing, they are likely not to be recent enough. +at the time of writing, they are likely not to be recent enough unless +the distribution tracks the latest releases. + +To easily check whether the requirements are met, the following target +can be used:: + + make LLVM=1 rustavailable + +This triggers the same logic used by Kconfig to determine whether +``RUST_IS_AVAILABLE`` should be enabled; but it also explains why not +if that is the case. rustc @@ -176,8 +186,8 @@ Configuration ------------- ``Rust support`` (``CONFIG_RUST``) needs to be enabled in the ``General setup`` -menu. The option is only shown if the build system can locate ``rustc``. -In turn, this will make visible the rest of options that depend on Rust. +menu. The option is only shown if a suitable Rust toolchain is found (see +above). In turn, this will make visible the rest of options that depend on Rust. Afterwards, go to:: diff --git a/MAINTAINERS b/MAINTAINERS index b6c447439803ca..5f3816c713fc48 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16591,6 +16591,7 @@ F: rust/ F: samples/rust/ F: Documentation/rust/ F: lib/rust.h +F: scripts/*rust* K: \b(?i:rust)\b RXRPC SOCKETS (AF_RXRPC) diff --git a/Makefile b/Makefile index a349e754eb2e62..a8eaba28db6b11 100644 --- a/Makefile +++ b/Makefile @@ -274,7 +274,7 @@ no-dot-config-targets := $(clean-targets) \ cscope gtags TAGS tags help% %docs check% coccicheck \ $(version_h) headers headers_% archheaders archscripts \ %asm-generic kernelversion %src-pkg dt_binding_check \ - outputmakefile rustfmt rustfmtcheck + outputmakefile rustavailable rustfmt rustfmtcheck # Installation targets should not require compiler. Unfortunately, vdso_install # is an exception where build artifacts may be updated. This must be fixed. no-compiler-targets := $(no-dot-config-targets) install dtbs_install \ @@ -1696,6 +1696,8 @@ help: @echo ' kselftest to existing .config.' @echo '' @echo 'Rust targets:' + @echo ' rustavailable - Checks whether the Rust toolchain is' + @echo ' available and, if not, explains why.' @echo ' rustfmt - Reformat all the Rust code in the kernel' @echo ' rustfmtcheck - Checks if all the Rust code in the kernel' @echo ' is formatted, printing a diff otherwise.' @@ -1781,6 +1783,11 @@ $(DOC_TARGETS): # Rust targets # --------------------------------------------------------------------------- +# "Is Rust available?" target +PHONY += rustavailable +rustavailable: + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust-is-available.sh -v + # Documentation target # # Using the singular to avoid running afoul of `no-dot-config-targets`. diff --git a/init/Kconfig b/init/Kconfig index cbbdd3a2c30fa5..c6497c816b2bae 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -60,14 +60,16 @@ config LLD_VERSION default $(ld-version) if LD_IS_LLD default 0 -config HAS_RUST - depends on ARM64 || CPU_32v6 || CPU_32v6K || (PPC64 && CPU_LITTLE_ENDIAN) || X86_64 || RISCV - def_bool $(success,$(RUSTC) --version) +config RUST_IS_AVAILABLE + def_bool $(success,$(srctree)/scripts/rust-is-available.sh) + help + This shows whether a suitable Rust toolchain is available (found). -config RUSTC_VERSION - depends on HAS_RUST - int - default $(shell,$(srctree)/scripts/rust-version.sh $(RUSTC)) + Please see Documentation/rust/quick-start.rst for instructions on how + to satify the build requirements of Rust support. + + In particular, the Makefile target 'rustavailable' is useful to check + why the Rust toolchain is not being detected. config CC_CAN_LINK bool @@ -2056,7 +2058,8 @@ config PROFILING config RUST bool "Rust support" - depends on HAS_RUST + depends on RUST_IS_AVAILABLE + depends on ARM64 || CPU_32v6 || CPU_32v6K || (PPC64 && CPU_LITTLE_ENDIAN) || X86_64 || RISCV depends on !COMPILE_TEST depends on !MODVERSIONS depends on !GCC_PLUGIN_RANDSTRUCT @@ -2074,6 +2077,16 @@ config RUST If unsure, say N. +config RUSTC_VERSION_TEXT + depends on RUST + string + default $(shell,$(RUSTC) --version) + +config BINDGEN_VERSION_TEXT + depends on RUST + string + default $(shell,$(BINDGEN) --version) + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/scripts/rust-is-available-bindgen-libclang.h b/scripts/rust-is-available-bindgen-libclang.h new file mode 100644 index 00000000000000..0ef6db10d67413 --- /dev/null +++ b/scripts/rust-is-available-bindgen-libclang.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#pragma message("clang version " __clang_version__) diff --git a/scripts/rust-is-available.sh b/scripts/rust-is-available.sh new file mode 100755 index 00000000000000..5741dcf64bc984 --- /dev/null +++ b/scripts/rust-is-available.sh @@ -0,0 +1,153 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Tests whether a suitable Rust toolchain is available. +# +# Pass `-v` for human output and more checks (as warnings). + +set -e + +min_tool_version=$(dirname $0)/min-tool-version.sh + +# Convert the version string x.y.z to a canonical up-to-7-digits form. +# +# Note that this function uses one more digit (compared to other +# instances in other version scripts) to give a bit more space to +# `rustc` since it will reach 1.100.0 in late 2026. +get_canonical_version() +{ + IFS=. + set -- $1 + echo $((100000 * $1 + 100 * $2 + $3)) +} + +# Check that the Rust compiler exists. +if ! command -v "$RUSTC" >/dev/null; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** Rust compiler '$RUSTC' could not be found." + echo >&2 "***" + fi + exit 1 +fi + +# Check that the Rust bindings generator exists. +if ! command -v "$BINDGEN" >/dev/null; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found." + echo >&2 "***" + fi + exit 1 +fi + +# Check that the Rust compiler version is suitable. +# +# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`. +rust_compiler_version=$("$RUSTC" --version | cut -f2 -d' ' | cut -f1 -d'-') +rust_compiler_min_version=$($min_tool_version rustc) +rust_compiler_cversion=$(get_canonical_version $rust_compiler_version) +rust_compiler_min_cversion=$(get_canonical_version $rust_compiler_min_version) +if [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** Rust compiler '$RUSTC' is too old." + echo >&2 "*** Your version: $rust_compiler_version" + echo >&2 "*** Minimum version: $rust_compiler_min_version" + echo >&2 "***" + fi + exit 1 +fi +if [ "$1" = -v ] && [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then + echo >&2 "***" + echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work." + echo >&2 "*** Your version: $rust_compiler_version" + echo >&2 "*** Expected version: $rust_compiler_min_version" + echo >&2 "***" +fi + +# Check that the Rust bindings generator is suitable. +# +# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`. +rust_bindings_generator_version=$("$BINDGEN" --version | cut -f2 -d' ' | cut -f1 -d'-') +rust_bindings_generator_min_version=$($min_tool_version bindgen) +rust_bindings_generator_cversion=$(get_canonical_version $rust_bindings_generator_version) +rust_bindings_generator_min_cversion=$(get_canonical_version $rust_bindings_generator_min_version) +if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cversion" ]; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' is too old." + echo >&2 "*** Your version: $rust_bindings_generator_version" + echo >&2 "*** Minimum version: $rust_bindings_generator_min_version" + echo >&2 "***" + fi + exit 1 +fi +if [ "$1" = -v ] && [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work." + echo >&2 "*** Your version: $rust_bindings_generator_version" + echo >&2 "*** Expected version: $rust_bindings_generator_min_version" + echo >&2 "***" +fi + +# Check that the `libclang` used by the Rust bindings generator is suitable. +# +# There may be a better way to do this in the future, +# see https://github.com/rust-lang/rust-bindgen/issues/2138 +bindgen_libclang_full_version=$(\ + "$BINDGEN" $(dirname $0)/rust-is-available-bindgen-libclang.h 2>&1 >/dev/null \ + | grep -F 'clang version' \ + | sed -E 's:^.*(clang version .*) \[.*$:\1:' \ +) +bindgen_libclang_version=$(echo "$bindgen_libclang_full_version" | cut -f3 -d' ') +bindgen_libclang_min_version=$($min_tool_version llvm) +bindgen_libclang_cversion=$(get_canonical_version $bindgen_libclang_version) +bindgen_libclang_min_cversion=$(get_canonical_version $bindgen_libclang_min_version) +if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old." + echo >&2 "*** Your version: $bindgen_libclang_version" + echo >&2 "*** Minimum version: $bindgen_libclang_min_version" + echo >&2 "***" + fi + exit 1 +fi + +# If the C compiler is Clang, then we can also check whether its full version +# matches exactly the `libclang` used by the Rust bindings generator. +if [ "$1" = -v ]; then + cc_name=$($(dirname $0)/cc-version.sh "$CC" | cut -f1 -d' ') + if [ "$cc_name" = Clang ]; then + clang_full_version=$("$CC" --version | grep -F 'clang version') + if [ "$clang_full_version" != "$bindgen_libclang_full_version" ]; then + echo >&2 "***" + echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') full version does not match Clang's. This may be a problem." + echo >&2 "*** libclang: $bindgen_libclang_full_version" + echo >&2 "*** Clang: $clang_full_version" + echo >&2 "***" + fi + fi +fi + +# Check that the source code for the `core` standard library exists. +# +# `$KRUSTFLAGS` is passed in case the user added `--sysroot`. +rustc_sysroot=$("$RUSTC" $KRUSTFLAGS --print sysroot) +rustc_src="$rustc_sysroot/lib/rustlib/src/rust/library" +rustc_src_core="$rustc_src/core/src/lib.rs" +if [ ! -e "$rustc_src_core" ]; then + if [ "$1" = -v ]; then + echo >&2 "***" + echo >&2 "*** Source code for the 'core' standard library could not be found" + echo >&2 "*** at '$rustc_src_core'." + echo >&2 "***" + fi + exit 1 +fi + +# Success! +if [ "$1" = -v ]; then + echo >&2 "Rust is available!" +fi diff --git a/scripts/rust-version.sh b/scripts/rust-version.sh deleted file mode 100755 index 67b6d31688e24b..00000000000000 --- a/scripts/rust-version.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0 -# -# rust-version rust-command -# -# Print the compiler version of `rust-command' in a 5 or 6-digit form -# such as `14502' for rustc-1.45.2 etc. -# -# Returns 0 if not found (so that Kconfig does not complain) -compiler="$*" - -if [ ${#compiler} -eq 0 ]; then - echo "Error: No compiler specified." >&2 - printf "Usage:\n\t$0 \n" >&2 - exit 1 -fi - -if ! command -v $compiler >/dev/null 2>&1; then - echo 0 - exit 0 -fi - -VERSION=$($compiler --version | cut -f2 -d' ') - -# Cut suffix if any (e.g. `-dev`) -VERSION=$(echo $VERSION | cut -f1 -d'-') - -MAJOR=$(echo $VERSION | cut -f1 -d'.') -MINOR=$(echo $VERSION | cut -f2 -d'.') -PATCHLEVEL=$(echo $VERSION | cut -f3 -d'.') -printf "%d%02d%02d\\n" $MAJOR $MINOR $PATCHLEVEL