162306a36Sopenharmony_ci#!/bin/sh
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# Tests whether a suitable Rust toolchain is available.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ciset -e
762306a36Sopenharmony_ci
862306a36Sopenharmony_cimin_tool_version=$(dirname $0)/min-tool-version.sh
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci# Convert the version string x.y.z to a canonical up-to-7-digits form.
1162306a36Sopenharmony_ci#
1262306a36Sopenharmony_ci# Note that this function uses one more digit (compared to other
1362306a36Sopenharmony_ci# instances in other version scripts) to give a bit more space to
1462306a36Sopenharmony_ci# `rustc` since it will reach 1.100.0 in late 2026.
1562306a36Sopenharmony_ciget_canonical_version()
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	IFS=.
1862306a36Sopenharmony_ci	set -- $1
1962306a36Sopenharmony_ci	echo $((100000 * $1 + 100 * $2 + $3))
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci# Print a reference to the Quick Start guide in the documentation.
2362306a36Sopenharmony_ciprint_docs_reference()
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	echo >&2 "***"
2662306a36Sopenharmony_ci	echo >&2 "*** Please see Documentation/rust/quick-start.rst for details"
2762306a36Sopenharmony_ci	echo >&2 "*** on how to set up the Rust support."
2862306a36Sopenharmony_ci	echo >&2 "***"
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci# Print an explanation about the fact that the script is meant to be called from Kbuild.
3262306a36Sopenharmony_ciprint_kbuild_explanation()
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	echo >&2 "***"
3562306a36Sopenharmony_ci	echo >&2 "*** This script is intended to be called from Kbuild."
3662306a36Sopenharmony_ci	echo >&2 "*** Please use the 'rustavailable' target to call it instead."
3762306a36Sopenharmony_ci	echo >&2 "*** Otherwise, the results may not be meaningful."
3862306a36Sopenharmony_ci	exit 1
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci# If the script fails for any reason, or if there was any warning, then
4262306a36Sopenharmony_ci# print a reference to the documentation on exit.
4362306a36Sopenharmony_ciwarning=0
4462306a36Sopenharmony_citrap 'if [ $? -ne 0 ] || [ $warning -ne 0 ]; then print_docs_reference; fi' EXIT
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci# Check that the expected environment variables are set.
4762306a36Sopenharmony_ciif [ -z "${RUSTC+x}" ]; then
4862306a36Sopenharmony_ci	echo >&2 "***"
4962306a36Sopenharmony_ci	echo >&2 "*** Environment variable 'RUSTC' is not set."
5062306a36Sopenharmony_ci	print_kbuild_explanation
5162306a36Sopenharmony_cifi
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciif [ -z "${BINDGEN+x}" ]; then
5462306a36Sopenharmony_ci	echo >&2 "***"
5562306a36Sopenharmony_ci	echo >&2 "*** Environment variable 'BINDGEN' is not set."
5662306a36Sopenharmony_ci	print_kbuild_explanation
5762306a36Sopenharmony_cifi
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciif [ -z "${CC+x}" ]; then
6062306a36Sopenharmony_ci	echo >&2 "***"
6162306a36Sopenharmony_ci	echo >&2 "*** Environment variable 'CC' is not set."
6262306a36Sopenharmony_ci	print_kbuild_explanation
6362306a36Sopenharmony_cifi
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci# Check that the Rust compiler exists.
6662306a36Sopenharmony_ciif ! command -v "$RUSTC" >/dev/null; then
6762306a36Sopenharmony_ci	echo >&2 "***"
6862306a36Sopenharmony_ci	echo >&2 "*** Rust compiler '$RUSTC' could not be found."
6962306a36Sopenharmony_ci	echo >&2 "***"
7062306a36Sopenharmony_ci	exit 1
7162306a36Sopenharmony_cifi
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci# Check that the Rust bindings generator exists.
7462306a36Sopenharmony_ciif ! command -v "$BINDGEN" >/dev/null; then
7562306a36Sopenharmony_ci	echo >&2 "***"
7662306a36Sopenharmony_ci	echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found."
7762306a36Sopenharmony_ci	echo >&2 "***"
7862306a36Sopenharmony_ci	exit 1
7962306a36Sopenharmony_cifi
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci# Check that the Rust compiler version is suitable.
8262306a36Sopenharmony_ci#
8362306a36Sopenharmony_ci# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
8462306a36Sopenharmony_cirust_compiler_output=$( \
8562306a36Sopenharmony_ci	LC_ALL=C "$RUSTC" --version 2>/dev/null
8662306a36Sopenharmony_ci) || rust_compiler_code=$?
8762306a36Sopenharmony_ciif [ -n "$rust_compiler_code" ]; then
8862306a36Sopenharmony_ci	echo >&2 "***"
8962306a36Sopenharmony_ci	echo >&2 "*** Running '$RUSTC' to check the Rust compiler version failed with"
9062306a36Sopenharmony_ci	echo >&2 "*** code $rust_compiler_code. See output and docs below for details:"
9162306a36Sopenharmony_ci	echo >&2 "***"
9262306a36Sopenharmony_ci	echo >&2 "$rust_compiler_output"
9362306a36Sopenharmony_ci	echo >&2 "***"
9462306a36Sopenharmony_ci	exit 1
9562306a36Sopenharmony_cifi
9662306a36Sopenharmony_cirust_compiler_version=$( \
9762306a36Sopenharmony_ci	echo "$rust_compiler_output" \
9862306a36Sopenharmony_ci		| sed -nE '1s:.*rustc ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
9962306a36Sopenharmony_ci)
10062306a36Sopenharmony_ciif [ -z "$rust_compiler_version" ]; then
10162306a36Sopenharmony_ci	echo >&2 "***"
10262306a36Sopenharmony_ci	echo >&2 "*** Running '$RUSTC' to check the Rust compiler version did not return"
10362306a36Sopenharmony_ci	echo >&2 "*** an expected output. See output and docs below for details:"
10462306a36Sopenharmony_ci	echo >&2 "***"
10562306a36Sopenharmony_ci	echo >&2 "$rust_compiler_output"
10662306a36Sopenharmony_ci	echo >&2 "***"
10762306a36Sopenharmony_ci	exit 1
10862306a36Sopenharmony_cifi
10962306a36Sopenharmony_cirust_compiler_min_version=$($min_tool_version rustc)
11062306a36Sopenharmony_cirust_compiler_cversion=$(get_canonical_version $rust_compiler_version)
11162306a36Sopenharmony_cirust_compiler_min_cversion=$(get_canonical_version $rust_compiler_min_version)
11262306a36Sopenharmony_ciif [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then
11362306a36Sopenharmony_ci	echo >&2 "***"
11462306a36Sopenharmony_ci	echo >&2 "*** Rust compiler '$RUSTC' is too old."
11562306a36Sopenharmony_ci	echo >&2 "***   Your version:    $rust_compiler_version"
11662306a36Sopenharmony_ci	echo >&2 "***   Minimum version: $rust_compiler_min_version"
11762306a36Sopenharmony_ci	echo >&2 "***"
11862306a36Sopenharmony_ci	exit 1
11962306a36Sopenharmony_cifi
12062306a36Sopenharmony_ciif [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
12162306a36Sopenharmony_ci	echo >&2 "***"
12262306a36Sopenharmony_ci	echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work."
12362306a36Sopenharmony_ci	echo >&2 "***   Your version:     $rust_compiler_version"
12462306a36Sopenharmony_ci	echo >&2 "***   Expected version: $rust_compiler_min_version"
12562306a36Sopenharmony_ci	echo >&2 "***"
12662306a36Sopenharmony_ci	warning=1
12762306a36Sopenharmony_cifi
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci# Check that the Rust bindings generator is suitable.
13062306a36Sopenharmony_ci#
13162306a36Sopenharmony_ci# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
13262306a36Sopenharmony_cirust_bindings_generator_output=$( \
13362306a36Sopenharmony_ci	LC_ALL=C "$BINDGEN" --version 2>/dev/null
13462306a36Sopenharmony_ci) || rust_bindings_generator_code=$?
13562306a36Sopenharmony_ciif [ -n "$rust_bindings_generator_code" ]; then
13662306a36Sopenharmony_ci	echo >&2 "***"
13762306a36Sopenharmony_ci	echo >&2 "*** Running '$BINDGEN' to check the Rust bindings generator version failed with"
13862306a36Sopenharmony_ci	echo >&2 "*** code $rust_bindings_generator_code. See output and docs below for details:"
13962306a36Sopenharmony_ci	echo >&2 "***"
14062306a36Sopenharmony_ci	echo >&2 "$rust_bindings_generator_output"
14162306a36Sopenharmony_ci	echo >&2 "***"
14262306a36Sopenharmony_ci	exit 1
14362306a36Sopenharmony_cifi
14462306a36Sopenharmony_cirust_bindings_generator_version=$( \
14562306a36Sopenharmony_ci	echo "$rust_bindings_generator_output" \
14662306a36Sopenharmony_ci		| sed -nE '1s:.*bindgen ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
14762306a36Sopenharmony_ci)
14862306a36Sopenharmony_ciif [ -z "$rust_bindings_generator_version" ]; then
14962306a36Sopenharmony_ci	echo >&2 "***"
15062306a36Sopenharmony_ci	echo >&2 "*** Running '$BINDGEN' to check the bindings generator version did not return"
15162306a36Sopenharmony_ci	echo >&2 "*** an expected output. See output and docs below for details:"
15262306a36Sopenharmony_ci	echo >&2 "***"
15362306a36Sopenharmony_ci	echo >&2 "$rust_bindings_generator_output"
15462306a36Sopenharmony_ci	echo >&2 "***"
15562306a36Sopenharmony_ci	exit 1
15662306a36Sopenharmony_cifi
15762306a36Sopenharmony_cirust_bindings_generator_min_version=$($min_tool_version bindgen)
15862306a36Sopenharmony_cirust_bindings_generator_cversion=$(get_canonical_version $rust_bindings_generator_version)
15962306a36Sopenharmony_cirust_bindings_generator_min_cversion=$(get_canonical_version $rust_bindings_generator_min_version)
16062306a36Sopenharmony_ciif [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cversion" ]; then
16162306a36Sopenharmony_ci	echo >&2 "***"
16262306a36Sopenharmony_ci	echo >&2 "*** Rust bindings generator '$BINDGEN' is too old."
16362306a36Sopenharmony_ci	echo >&2 "***   Your version:    $rust_bindings_generator_version"
16462306a36Sopenharmony_ci	echo >&2 "***   Minimum version: $rust_bindings_generator_min_version"
16562306a36Sopenharmony_ci	echo >&2 "***"
16662306a36Sopenharmony_ci	exit 1
16762306a36Sopenharmony_cifi
16862306a36Sopenharmony_ciif [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
16962306a36Sopenharmony_ci	echo >&2 "***"
17062306a36Sopenharmony_ci	echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work."
17162306a36Sopenharmony_ci	echo >&2 "***   Your version:     $rust_bindings_generator_version"
17262306a36Sopenharmony_ci	echo >&2 "***   Expected version: $rust_bindings_generator_min_version"
17362306a36Sopenharmony_ci	echo >&2 "***"
17462306a36Sopenharmony_ci	warning=1
17562306a36Sopenharmony_cifi
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci# Check that the `libclang` used by the Rust bindings generator is suitable.
17862306a36Sopenharmony_ci#
17962306a36Sopenharmony_ci# In order to do that, first invoke `bindgen` to get the `libclang` version
18062306a36Sopenharmony_ci# found by `bindgen`. This step may already fail if, for instance, `libclang`
18162306a36Sopenharmony_ci# is not found, thus inform the user in such a case.
18262306a36Sopenharmony_cibindgen_libclang_output=$( \
18362306a36Sopenharmony_ci	LC_ALL=C "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang.h 2>&1 >/dev/null
18462306a36Sopenharmony_ci) || bindgen_libclang_code=$?
18562306a36Sopenharmony_ciif [ -n "$bindgen_libclang_code" ]; then
18662306a36Sopenharmony_ci	echo >&2 "***"
18762306a36Sopenharmony_ci	echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
18862306a36Sopenharmony_ci	echo >&2 "*** bindings generator) failed with code $bindgen_libclang_code. This may be caused by"
18962306a36Sopenharmony_ci	echo >&2 "*** a failure to locate libclang. See output and docs below for details:"
19062306a36Sopenharmony_ci	echo >&2 "***"
19162306a36Sopenharmony_ci	echo >&2 "$bindgen_libclang_output"
19262306a36Sopenharmony_ci	echo >&2 "***"
19362306a36Sopenharmony_ci	exit 1
19462306a36Sopenharmony_cifi
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci# `bindgen` returned successfully, thus use the output to check that the version
19762306a36Sopenharmony_ci# of the `libclang` found by the Rust bindings generator is suitable.
19862306a36Sopenharmony_ci#
19962306a36Sopenharmony_ci# Unlike other version checks, note that this one does not necessarily appear
20062306a36Sopenharmony_ci# in the first line of the output, thus no `sed` address is provided.
20162306a36Sopenharmony_cibindgen_libclang_version=$( \
20262306a36Sopenharmony_ci	echo "$bindgen_libclang_output" \
20362306a36Sopenharmony_ci		| sed -nE 's:.*clang version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
20462306a36Sopenharmony_ci)
20562306a36Sopenharmony_ciif [ -z "$bindgen_libclang_version" ]; then
20662306a36Sopenharmony_ci	echo >&2 "***"
20762306a36Sopenharmony_ci	echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
20862306a36Sopenharmony_ci	echo >&2 "*** bindings generator) did not return an expected output. See output"
20962306a36Sopenharmony_ci	echo >&2 "*** and docs below for details:"
21062306a36Sopenharmony_ci	echo >&2 "***"
21162306a36Sopenharmony_ci	echo >&2 "$bindgen_libclang_output"
21262306a36Sopenharmony_ci	echo >&2 "***"
21362306a36Sopenharmony_ci	exit 1
21462306a36Sopenharmony_cifi
21562306a36Sopenharmony_cibindgen_libclang_min_version=$($min_tool_version llvm)
21662306a36Sopenharmony_cibindgen_libclang_cversion=$(get_canonical_version $bindgen_libclang_version)
21762306a36Sopenharmony_cibindgen_libclang_min_cversion=$(get_canonical_version $bindgen_libclang_min_version)
21862306a36Sopenharmony_ciif [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
21962306a36Sopenharmony_ci	echo >&2 "***"
22062306a36Sopenharmony_ci	echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old."
22162306a36Sopenharmony_ci	echo >&2 "***   Your version:    $bindgen_libclang_version"
22262306a36Sopenharmony_ci	echo >&2 "***   Minimum version: $bindgen_libclang_min_version"
22362306a36Sopenharmony_ci	echo >&2 "***"
22462306a36Sopenharmony_ci	exit 1
22562306a36Sopenharmony_cifi
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci# If the C compiler is Clang, then we can also check whether its version
22862306a36Sopenharmony_ci# matches the `libclang` version used by the Rust bindings generator.
22962306a36Sopenharmony_ci#
23062306a36Sopenharmony_ci# In the future, we might be able to perform a full version check, see
23162306a36Sopenharmony_ci# https://github.com/rust-lang/rust-bindgen/issues/2138.
23262306a36Sopenharmony_cicc_name=$($(dirname $0)/cc-version.sh $CC | cut -f1 -d' ')
23362306a36Sopenharmony_ciif [ "$cc_name" = Clang ]; then
23462306a36Sopenharmony_ci	clang_version=$( \
23562306a36Sopenharmony_ci		LC_ALL=C $CC --version 2>/dev/null \
23662306a36Sopenharmony_ci			| sed -nE '1s:.*version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
23762306a36Sopenharmony_ci	)
23862306a36Sopenharmony_ci	if [ "$clang_version" != "$bindgen_libclang_version" ]; then
23962306a36Sopenharmony_ci		echo >&2 "***"
24062306a36Sopenharmony_ci		echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN')"
24162306a36Sopenharmony_ci		echo >&2 "*** version does not match Clang's. This may be a problem."
24262306a36Sopenharmony_ci		echo >&2 "***   libclang version: $bindgen_libclang_version"
24362306a36Sopenharmony_ci		echo >&2 "***   Clang version:    $clang_version"
24462306a36Sopenharmony_ci		echo >&2 "***"
24562306a36Sopenharmony_ci		warning=1
24662306a36Sopenharmony_ci	fi
24762306a36Sopenharmony_cifi
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci# Check that the source code for the `core` standard library exists.
25062306a36Sopenharmony_ci#
25162306a36Sopenharmony_ci# `$KRUSTFLAGS` is passed in case the user added `--sysroot`.
25262306a36Sopenharmony_cirustc_sysroot=$("$RUSTC" $KRUSTFLAGS --print sysroot)
25362306a36Sopenharmony_cirustc_src=${RUST_LIB_SRC:-"$rustc_sysroot/lib/rustlib/src/rust/library"}
25462306a36Sopenharmony_cirustc_src_core="$rustc_src/core/src/lib.rs"
25562306a36Sopenharmony_ciif [ ! -e "$rustc_src_core" ]; then
25662306a36Sopenharmony_ci	echo >&2 "***"
25762306a36Sopenharmony_ci	echo >&2 "*** Source code for the 'core' standard library could not be found"
25862306a36Sopenharmony_ci	echo >&2 "*** at '$rustc_src_core'."
25962306a36Sopenharmony_ci	echo >&2 "***"
26062306a36Sopenharmony_ci	exit 1
26162306a36Sopenharmony_cifi
262