1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# kselftest_deps.sh 4# 5# Checks for kselftest build dependencies on the build system. 6# Copyright (c) 2020 Shuah Khan <skhan@linuxfoundation.org> 7# 8# 9 10usage() 11{ 12 13echo -e "Usage: $0 -[p] <compiler> [test_name]\n" 14echo -e "\tkselftest_deps.sh [-p] gcc" 15echo -e "\tkselftest_deps.sh [-p] gcc mm" 16echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc" 17echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc mm\n" 18echo "- Should be run in selftests directory in the kernel repo." 19echo "- Checks if Kselftests can be built/cross-built on a system." 20echo "- Parses all test/sub-test Makefile to find library dependencies." 21echo "- Runs compile test on a trivial C file with LDLIBS specified" 22echo " in the test Makefiles to identify missing library dependencies." 23echo "- Prints suggested target list for a system filtering out tests" 24echo " failed the build dependency check from the TARGETS in Selftests" 25echo " main Makefile when optional -p is specified." 26echo "- Prints pass/fail dependency check for each tests/sub-test." 27echo "- Prints pass/fail targets and libraries." 28echo "- Default: runs dependency checks on all tests." 29echo "- Optional: test name can be specified to check dependencies for it." 30exit 1 31 32} 33 34# Start main() 35main() 36{ 37 38base_dir=`pwd` 39# Make sure we're in the selftests top-level directory. 40if [ $(basename "$base_dir") != "selftests" ]; then 41 echo -e "\tPlease run $0 in" 42 echo -e "\ttools/testing/selftests directory ..." 43 exit 1 44fi 45 46print_targets=0 47 48while getopts "p" arg; do 49 case $arg in 50 p) 51 print_targets=1 52 shift;; 53 esac 54done 55 56if [ $# -eq 0 ] 57then 58 usage 59fi 60 61# Compiler 62CC=$1 63 64tmp_file=$(mktemp).c 65trap "rm -f $tmp_file.o $tmp_file $tmp_file.bin" EXIT 66#echo $tmp_file 67 68pass=$(mktemp).out 69trap "rm -f $pass" EXIT 70#echo $pass 71 72fail=$(mktemp).out 73trap "rm -f $fail" EXIT 74#echo $fail 75 76# Generate tmp source fire for compile test 77cat << "EOF" > $tmp_file 78int main() 79{ 80} 81EOF 82 83# Save results 84total_cnt=0 85fail_trgts=() 86fail_libs=() 87fail_cnt=0 88pass_trgts=() 89pass_libs=() 90pass_cnt=0 91 92# Get all TARGETS from selftests Makefile 93targets=$(grep -E "^TARGETS +|^TARGETS =" Makefile | cut -d "=" -f2) 94 95# Initially, in LDLIBS related lines, the dep checker needs 96# to ignore lines containing the following strings: 97filter="\$(VAR_LDLIBS)\|pkg-config\|PKG_CONFIG\|IOURING_EXTRA_LIBS" 98 99# Single test case 100if [ $# -eq 2 ] 101then 102 test=$2/Makefile 103 104 l1_test $test 105 l2_test $test 106 l3_test $test 107 l4_test $test 108 l5_test $test 109 110 print_results $1 $2 111 exit $? 112fi 113 114# Level 1: LDLIBS set static. 115# 116# Find all LDLIBS set statically for all executables built by a Makefile 117# and filter out VAR_LDLIBS to discard the following: 118# gpio/Makefile:LDLIBS += $(VAR_LDLIBS) 119# Append space at the end of the list to append more tests. 120 121l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \ 122 grep -v "$filter" | awk -F: '{print $1}' | uniq) 123 124# Level 2: LDLIBS set dynamically. 125# 126# Level 2 127# Some tests have multiple valid LDLIBS lines for individual sub-tests 128# that need dependency checks. Find them and append them to the tests 129# e.g: mm/Makefile:$(OUTPUT)/userfaultfd: LDLIBS += -lpthread 130# Filter out VAR_LDLIBS to discard the following: 131# memfd/Makefile:$(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS) 132# Append space at the end of the list to append more tests. 133 134l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ 135 grep -v "$filter" | awk -F: '{print $1}' | uniq) 136 137# Level 3 138# memfd and others use pkg-config to find mount and fuse libs 139# respectively and save it in VAR_LDLIBS. If pkg-config doesn't find 140# any, VAR_LDLIBS set to default. 141# Use the default value and filter out pkg-config for dependency check. 142# e.g: 143# memfd/Makefile 144# VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null) 145 146l3_tests=$(grep -r --include=Makefile "^VAR_LDLIBS" | \ 147 grep -v "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) 148 149# Level 4 150# some tests may fall back to default using `|| echo -l<libname>` 151# if pkg-config doesn't find the libs, instead of using VAR_LDLIBS 152# as per level 3 checks. 153# e.g: 154# netfilter/Makefile 155# LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) 156l4_tests=$(grep -r --include=Makefile "^LDLIBS" | \ 157 grep "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) 158 159# Level 5 160# some tests may use IOURING_EXTRA_LIBS to add extra libs to LDLIBS, 161# which in turn may be defined in a sub-Makefile 162# e.g.: 163# mm/Makefile 164# $(OUTPUT)/gup_longterm: LDLIBS += $(IOURING_EXTRA_LIBS) 165l5_tests=$(grep -r --include=Makefile "LDLIBS +=.*\$(IOURING_EXTRA_LIBS)" | \ 166 awk -F: '{print $1}' | uniq) 167 168#echo l1_tests $l1_tests 169#echo l2_tests $l2_tests 170#echo l3_tests $l3_tests 171#echo l4_tests $l4_tests 172#echo l5_tests $l5_tests 173 174all_tests 175print_results $1 $2 176 177exit $? 178} 179# end main() 180 181all_tests() 182{ 183 for test in $l1_tests; do 184 l1_test $test 185 done 186 187 for test in $l2_tests; do 188 l2_test $test 189 done 190 191 for test in $l3_tests; do 192 l3_test $test 193 done 194 195 for test in $l4_tests; do 196 l4_test $test 197 done 198 199 for test in $l5_tests; do 200 l5_test $test 201 done 202} 203 204# Use same parsing used for l1_tests and pick libraries this time. 205l1_test() 206{ 207 test_libs=$(grep --include=Makefile "^LDLIBS" $test | \ 208 grep -v "$filter" | \ 209 sed -e 's/\:/ /' | \ 210 sed -e 's/+/ /' | cut -d "=" -f 2) 211 212 check_libs $test $test_libs 213} 214 215# Use same parsing used for l2_tests and pick libraries this time. 216l2_test() 217{ 218 test_libs=$(grep --include=Makefile ": LDLIBS" $test | \ 219 grep -v "$filter" | \ 220 sed -e 's/\:/ /' | sed -e 's/+/ /' | \ 221 cut -d "=" -f 2) 222 223 check_libs $test $test_libs 224} 225 226l3_test() 227{ 228 test_libs=$(grep --include=Makefile "^VAR_LDLIBS" $test | \ 229 grep -v "pkg-config" | sed -e 's/\:/ /' | 230 sed -e 's/+/ /' | cut -d "=" -f 2) 231 232 check_libs $test $test_libs 233} 234 235l4_test() 236{ 237 test_libs=$(grep --include=Makefile "^VAR_LDLIBS\|^LDLIBS" $test | \ 238 grep "\(pkg-config\|PKG_CONFIG\).*|| echo " | \ 239 sed -e 's/.*|| echo //' | sed -e 's/)$//') 240 241 check_libs $test $test_libs 242} 243 244l5_test() 245{ 246 tests=$(find $(dirname "$test") -type f -name "*.mk") 247 test_libs=$(grep "^IOURING_EXTRA_LIBS +\?=" $tests | \ 248 cut -d "=" -f 2) 249 250 check_libs $test $test_libs 251} 252 253check_libs() 254{ 255 256if [[ ! -z "${test_libs// }" ]] 257then 258 259 #echo $test_libs 260 261 for lib in $test_libs; do 262 263 let total_cnt+=1 264 $CC -o $tmp_file.bin $lib $tmp_file > /dev/null 2>&1 265 if [ $? -ne 0 ]; then 266 echo "FAIL: $test dependency check: $lib" >> $fail 267 let fail_cnt+=1 268 fail_libs+="$lib " 269 fail_target=$(echo "$test" | cut -d "/" -f1) 270 fail_trgts+="$fail_target " 271 targets=$(echo "$targets" | grep -v "$fail_target") 272 else 273 echo "PASS: $test dependency check passed $lib" >> $pass 274 let pass_cnt+=1 275 pass_libs+="$lib " 276 pass_trgts+="$(echo "$test" | cut -d "/" -f1) " 277 fi 278 279 done 280fi 281} 282 283print_results() 284{ 285 echo -e "========================================================"; 286 echo -e "Kselftest Dependency Check for [$0 $1 $2] results..." 287 288 if [ $print_targets -ne 0 ] 289 then 290 echo -e "Suggested Selftest Targets for your configuration:" 291 echo -e "$targets"; 292 fi 293 294 echo -e "========================================================"; 295 echo -e "Checked tests defining LDLIBS dependencies" 296 echo -e "--------------------------------------------------------"; 297 echo -e "Total tests with Dependencies:" 298 echo -e "$total_cnt Pass: $pass_cnt Fail: $fail_cnt"; 299 300 if [ $pass_cnt -ne 0 ]; then 301 echo -e "--------------------------------------------------------"; 302 cat $pass 303 echo -e "--------------------------------------------------------"; 304 echo -e "Targets passed build dependency check on system:" 305 echo -e "$(echo "$pass_trgts" | xargs -n1 | sort -u | xargs)" 306 fi 307 308 if [ $fail_cnt -ne 0 ]; then 309 echo -e "--------------------------------------------------------"; 310 cat $fail 311 echo -e "--------------------------------------------------------"; 312 echo -e "Targets failed build dependency check on system:" 313 echo -e "$(echo "$fail_trgts" | xargs -n1 | sort -u | xargs)" 314 echo -e "--------------------------------------------------------"; 315 echo -e "Missing libraries system" 316 echo -e "$(echo "$fail_libs" | xargs -n1 | sort -u | xargs)" 317 fi 318 319 echo -e "--------------------------------------------------------"; 320 echo -e "========================================================"; 321} 322 323main "$@" 324