diff --git a/rust-features.sh b/rust-features.sh new file mode 100755 index 0000000..fbd4ea8 --- /dev/null +++ b/rust-features.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +############################################################################## +# +# This script checks that crates to not carelessly enable features that +# should stay disabled. It's important to check that since features +# are used to gate specific functionality which should only be enabled +# when the feature is explicitly enabled. +# +# Invocation scheme: +# ./rust-features.sh +# +# Example: +# ./rust-features.sh path/to/substrate +# +# The steps of this script: +# 1. Check that all required dependencies are installed. +# 2. Check that all rules are fullfilled for the whole workspace. If not: +# 4. Check all crates to find the offending ones. +# 5. Print all offending crates and exit with code 1. +# +############################################################################## + +set -eu + +# Check that cargo and grep are installed - otherwise abort. +command -v cargo >/dev/null 2>&1 || { echo >&2 "cargo is required but not installed. Aborting."; exit 1; } +command -v grep >/dev/null 2>&1 || { echo >&2 "grep is required but not installed. Aborting."; exit 1; } + +# Enter the workspace root folder. +cd "$1" +echo "Workspace root is $PWD" + +function main() { + feature_does_not_imply 'default' 'runtime-benchmarks' + feature_does_not_imply 'std' 'runtime-benchmarks' + feature_does_not_imply 'default' 'try-runtime' + feature_does_not_imply 'std' 'try-runtime' +} + +# Accepts two feature names as arguments. +# Checks that the first feature does not imply the second one. +function feature_does_not_imply() { + ENABLED=$1 + STAYS_DISABLED=$2 + echo "📏 Checking that $ENABLED does not imply $STAYS_DISABLED ..." + + # Check if the forbidden feature is enabled anywhere in the workspace. + if cargo tree --no-default-features --locked --workspace -e features --features "$ENABLED" | grep -qF "feature \"$STAYS_DISABLED\""; then + echo "❌ $ENABLED implies $STAYS_DISABLED in the workspace" + else + echo "✅ $ENABLED does not imply $STAYS_DISABLED in the workspace" + return + fi + + # Find all Cargo.toml files but exclude the root one since we already know that it is broken. + CARGOS=`find . -name Cargo.toml -not -path ./Cargo.toml` + NUM_CRATES=`echo "$CARGOS" | wc -l` + FAILED=0 + PASSED=0 + echo "🔍 Checking all $NUM_CRATES crates - this takes some time." + + for CARGO in $CARGOS; do + OUTPUT=$(cargo tree --no-default-features --locked --offline -e features --features $ENABLED --manifest-path $CARGO 2>&1 || true) + + if echo "$OUTPUT" | grep -qF "not supported for packages in this workspace"; then + # This case just means that the pallet does not support the + # requested feature which is fine. + PASSED=$((PASSED+1)) + elif echo "$OUTPUT" | grep -qF "feature \"$STAYS_DISABLED\""; then + echo "❌ Violation in $CARGO by dependency:" + # Best effort hint for which dependency needs to be fixed. + echo "$OUTPUT" | grep -wF "feature \"$STAYS_DISABLED\"" | head -n 1 + FAILED=$((FAILED+1)) + else + PASSED=$((PASSED+1)) + fi + done + + echo "Checked $NUM_CRATES crates in total of which $FAILED failed and $PASSED passed." + echo "Exiting with code 1" + exit 1 +} + +main "$@"