diff --git a/autobuild.sh b/autobuild.sh index 5ea44cad85..52541f1e92 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -77,9 +77,7 @@ timing_enter "$make_timing_label" $MAKE $MAKEFLAGS clean if [ $SPDK_BUILD_SHARED_OBJECT -eq 1 ]; then - ./configure $config_params --with-shared - $MAKE $MAKEFLAGS - $MAKE $MAKEFLAGS clean + $rootdir/test/make/check_so_deps.sh report_test_completion "shared_object_build" fi diff --git a/test/make/check_so_deps.sh b/test/make/check_so_deps.sh new file mode 100755 index 0000000000..afc8f4df29 --- /dev/null +++ b/test/make/check_so_deps.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash + +if [ "$(uname -s)" = "FreeBSD" ]; then + echo "Not testing for shared object dependencies on FreeBSD." + exit 0 +fi + +rootdir=$(readlink -f $(dirname $0)/../..) +source "$rootdir/test/common/autotest_common.sh" + +libdir="$rootdir/build/lib" +libdeps_file="$rootdir/mk/spdk.lib_deps.mk" + +# This function is needed to properly evaluate the Make variables into actual dependencies. +function replace_defined_variables() { + local arr=("$@") + local bad_values=() + local good_values=() + local new_values + for dep in "${arr[@]}"; do + if [[ $dep == *'$'* ]]; then + raw_dep=${dep/$\(/} + raw_dep=${raw_dep/\)/ } + bad_values+=("$raw_dep") + else + good_values+=("$dep") + fi + done + for dep in "${bad_values[@]}"; do + dep_def_arr=($(cat $libdeps_file | grep -v "#" | grep "${dep}" | cut -d "=" -f 2 | xargs)) + new_values=($(replace_defined_variables "${dep_def_arr[@]}")) + good_values=( "${good_values[@]}" "${new_values[@]}" ) + done + echo ${good_values[*]} +} + +function confirm_deps() { + lib=$1 + missing_syms=() + dep_names=() + found_symbol_lib="" + + #keep the space here to differentiate bdev and bdev_* + lib_shortname=$(basename $lib | sed 's,libspdk_,,g' | sed 's,\.so, ,g') + lib_make_deps=($(cat $libdeps_file | grep "DEPDIRS-${lib_shortname}" | cut -d "=" -f 2 | xargs)) + lib_make_deps=($(replace_defined_variables "${lib_make_deps[@]}")) + + for dep in ${lib_make_deps[@]}; do + if [[ $dep == *'$'* ]]; then + dep_no_dollar=$(echo $dep | sed 's,$(,,g' | sed 's,),,g') + fi + done + + symbols=$(readelf -s $lib | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort | uniq) + for symbol in $symbols; do + for deplib in $DEP_LIBS; do + if [ "$deplib" == "$lib" ]; then + continue + fi + found_symbol=$(readelf -s $deplib | grep -E "DEFAULT\s+[0-9]+\s$symbol$") || true + if [ "$found_symbol" != "" ]; then + found_symbol_lib=$(basename $deplib | sed 's,libspdk_,,g' | sed 's,\.so,,g') + break + fi + done + if [ "$found_symbol" == "" ]; then + missing_syms+=("$symbol") + else + dep_names+=("$found_symbol_lib") + fi + done + IFS=$'\n' + # Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf. + lib_make_deps=( $(printf "%s\n" ${lib_make_deps[@]} | sort | grep -v "event_") ) + # Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it. + dep_names=( $(printf "%s\n" ${dep_names[@]} | sort | uniq | grep -v "env_dpdk") ) + unset IFS + diff=$(echo ${dep_names[@]} ${lib_make_deps[@]} | tr ' ' '\n' | sort | uniq -u) + if [ "$diff" != "" ]; then + touch $fail_file + echo "there was a dependency mismatch in the library $lib_shortname" + echo "The makefile lists: '${lib_make_deps[*]}'" + echo "readelf outputs : '${dep_names[*]}'" + echo "---------------------------------------------------------------------" + elif [ ${#missing_syms[@]} -ne 0 ]; then + echo "There are still undefined symbols in the library $lib_shortname" + printf "%s\n" ${missing_syms[@]} + echo "---------------------------------------------------------------------" + fi +} + +# By removing the spdk.lib_deps.mk file from spdk.lib.mk, we ensure that we won't +# create any link dependencies. Then we can be sure we get a valid accounting of the +# symbol dependencies we have. +sed -i -e 's,include $(SPDK_ROOT_DIR)/mk/spdk.lib_deps.mk,,g' "$rootdir/mk/spdk.lib.mk" + +if [ "$SPDK_TEST_OCF" -eq 1 ]; then + config_params="$config_params --with-ocf=$rootdir/build/ocf.a" +fi + +./configure $config_params --with-shared +$MAKE $MAKEFLAGS + +xtrace_disable + +echo "---------------------------------------------------------------------" +# Exclude libspdk_env_dpdk.so from the library list. We don't link against this one so that +# users can define their own environment abstraction. However we do want to still check it +# for dependencies to avoid printing out a bunch of confusing symbols under the missing +# symbols section. +SPDK_LIBS=$(ls -1 $libdir/libspdk_*.so | grep -v libspdk_env_dpdk.so) +DEP_LIBS=$(ls -1 $libdir/libspdk_*.so) + +fail_file=$output_dir/check_so_deps_fail + +rm -f $fail_file + +for lib in $SPDK_LIBS; do + confirm_deps $lib& +done + +wait + +$MAKE $MAKEFLAGS clean +git checkout "$rootdir/mk/spdk.lib.mk" + +if [ -f $fail_file ]; then + rm -f $fail_file + echo "shared object test failed" + exit 1 +fi + +xtrace_restore