Skip to content

Commit

Permalink
rust: rework Rust toolchain detection
Browse files Browse the repository at this point in the history
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 cf00942
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 43 deletions.
16 changes: 13 additions & 3 deletions Documentation/rust/quick-start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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::

Expand Down
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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.'
Expand Down Expand Up @@ -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`.
Expand Down
29 changes: 21 additions & 8 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions scripts/rust-is-available-bindgen-libclang.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma message("clang version " __clang_version__)
153 changes: 153 additions & 0 deletions scripts/rust-is-available.sh
Original file line number Diff line number Diff line change
@@ -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
31 changes: 0 additions & 31 deletions scripts/rust-version.sh

This file was deleted.

0 comments on commit cf00942

Please sign in to comment.