Skip to content
Permalink
Browse files
rust: rework Rust toolchain detection
The name of the `HAS_RUST` option has been renamed to
`RUST_IS_AVAILABLE`, and now encompasses more checks to
see whether a suitable Rust toolchain is available for
compiling the kernel.

This will allow us later to remove the `!COMPILE_TEST` bound,
which, in turn, will allow to e.g. eventually start testing
the Rust support in places that do `all{yes,mod}config`.

This also provides an easy way for users/developers to
understand why the Rust toolchain might not be available:
a Makefile target is provided which explains why that
is the case, using the same logic that Kbuild/Kconfig use;
which addresses some of the feedback we got.

Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
  • Loading branch information
ojeda committed Jan 15, 2022
1 parent 1292114 commit cf009425efb0d9533b5bf09ebd304b938e4e5dc2
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 43 deletions.
@@ -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::

@@ -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)
@@ -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`.
@@ -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.
@@ -0,0 +1,2 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma message("clang version " __clang_version__)
@@ -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

This file was deleted.

0 comments on commit cf00942

Please sign in to comment.