cf091238f5
Upon running abidiff to get the list of impacted interfaces it seems to exit with != 0 status. This triggers errexit causing the script to exit in the middle of processing the lib files so we don't get the full picture of all potentially impacted files. Ignore the exit status in this case and allow the loop to go through all the .sos. Signed-off-by: Michal Berger <michalx.berger@intel.com> Change-Id: I7edc5122161b0927fdd9a918571419f32fd46dac Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7815 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Paul Luse <paul.e.luse@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
278 lines
8.1 KiB
Bash
Executable File
278 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
shopt -s extglob
|
|
|
|
function get_git_tag() {
|
|
git -C "${1:-$rootdir}" describe --tags --abbrev=0 --exclude=LTS
|
|
}
|
|
|
|
if [ "$(uname -s)" = "FreeBSD" ]; then
|
|
echo "Not testing for shared object dependencies on FreeBSD."
|
|
exit 0
|
|
fi
|
|
|
|
rootdir=$(readlink -f $(dirname $0)/../..)
|
|
|
|
if [[ ! -f $1 ]]; then
|
|
echo "ERROR: SPDK test configuration not specified"
|
|
exit 1
|
|
fi
|
|
|
|
source $1
|
|
source "$rootdir/test/common/autotest_common.sh"
|
|
|
|
libdir="$rootdir/build/lib"
|
|
libdeps_file="$rootdir/mk/spdk.lib_deps.mk"
|
|
suppression_file="$HOME/abigail_suppressions.ini"
|
|
|
|
spdk_tag=$(get_git_tag)
|
|
spdk_lts_tag=$(get_git_tag "$HOME/spdk_abi_lts")
|
|
repo="spdk_abi_latest"
|
|
if [[ "$spdk_tag" == "$spdk_lts_tag" ]]; then
|
|
repo="spdk_abi_lts"
|
|
fi
|
|
source_abi_dir="$HOME/$repo/build/lib"
|
|
|
|
function confirm_abi_deps() {
|
|
local processed_so=0
|
|
local abidiff_output
|
|
|
|
echo "* Running ${FUNCNAME[0]} against $repo" >&2
|
|
|
|
if ! hash abidiff; then
|
|
echo "Unable to check ABI compatibility. Please install abidiff."
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -d $source_abi_dir ]; then
|
|
echo "No source ABI available, failing this test."
|
|
return 1
|
|
fi
|
|
|
|
cat << EOF > ${suppression_file}
|
|
[suppress_type]
|
|
name = spdk_nvme_ns_data
|
|
[suppress_type]
|
|
name = spdk_nvme_ctrlr_data
|
|
EOF
|
|
|
|
for object in "$libdir"/libspdk_*.so; do
|
|
abidiff_output=0
|
|
|
|
so_file=$(basename $object)
|
|
if [ ! -f "$source_abi_dir/$so_file" ]; then
|
|
echo "No corresponding object for $so_file in canonical directory. Skipping."
|
|
continue
|
|
fi
|
|
|
|
cmd_args=('abidiff'
|
|
$source_abi_dir/$so_file $libdir/$so_file
|
|
'--headers-dir1' $source_abi_dir/../../include
|
|
'--headers-dir2' $rootdir/include
|
|
'--leaf-changes-only' '--suppressions' $suppression_file)
|
|
|
|
if ! output=$("${cmd_args[@]}" --stat); then
|
|
# remove any filtered out variables.
|
|
output=$(sed "s/ [()][^)]*[)]//g" <<< "$output")
|
|
|
|
IFS="." read -r _ _ new_so_maj new_so_min < <(readlink "$libdir/$so_file")
|
|
IFS="." read -r _ _ old_so_maj old_so_min < <(readlink "$source_abi_dir/$so_file")
|
|
|
|
found_abi_change=false
|
|
so_name_changed=no
|
|
|
|
if [[ $output == *"ELF SONAME changed"* ]]; then
|
|
so_name_changed=yes
|
|
fi
|
|
|
|
changed_leaf_types=0
|
|
if [[ $output =~ "leaf types summary: "([0-9]+) ]]; then
|
|
changed_leaf_types=${BASH_REMATCH[1]}
|
|
fi
|
|
|
|
removed_functions=0 changed_functions=0 added_functions=0
|
|
if [[ $output =~ "functions summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then
|
|
removed_functions=${BASH_REMATCH[1]} changed_functions=${BASH_REMATCH[2]} added_functions=${BASH_REMATCH[3]}
|
|
fi
|
|
|
|
removed_vars=0 changed_vars=0 added_vars=0
|
|
if [[ $output =~ "variables summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then
|
|
removed_vars=${BASH_REMATCH[1]} changed_vars=${BASH_REMATCH[2]} added_vars=${BASH_REMATCH[3]}
|
|
fi
|
|
|
|
if ((changed_leaf_types != 0)); then
|
|
if ((new_so_maj == old_so_maj)); then
|
|
abidiff_output=1
|
|
touch $fail_file
|
|
echo "Please update the major SO version for $so_file. A header accessible type has been modified since last release."
|
|
fi
|
|
found_abi_change=true
|
|
fi
|
|
|
|
if ((removed_functions != 0)) || ((removed_vars != 0)); then
|
|
if ((new_so_maj == old_so_maj)); then
|
|
abidiff_output=1
|
|
touch $fail_file
|
|
echo "Please update the major SO version for $so_file. API functions or variables have been removed since last release."
|
|
fi
|
|
found_abi_change=true
|
|
fi
|
|
|
|
if ((changed_functions != 0)) || ((changed_vars != 0)); then
|
|
if ((new_so_maj == old_so_maj)); then
|
|
abidiff_output=1
|
|
touch $fail_file
|
|
echo "Please update the major SO version for $so_file. API functions or variables have been changed since last release."
|
|
fi
|
|
found_abi_change=true
|
|
fi
|
|
|
|
if ((added_functions != 0)) || ((added_vars != 0)); then
|
|
if ((new_so_min == old_so_min && new_so_maj == old_so_maj)) && ! $found_abi_change; then
|
|
abidiff_output=1
|
|
touch $fail_file
|
|
echo "Please update the minor SO version for $so_file. API functions or variables have been added since last release."
|
|
fi
|
|
found_abi_change=true
|
|
fi
|
|
|
|
if [[ $so_name_changed == yes ]]; then
|
|
if ! $found_abi_change; then
|
|
echo "SO name for $so_file changed without a change to abi. please revert that change."
|
|
touch $fail_file
|
|
fi
|
|
|
|
if ((new_so_maj != old_so_maj && new_so_min != 0)); then
|
|
echo "SO major version for $so_file was bumped. Please reset the minor version to 0."
|
|
touch $fail_file
|
|
fi
|
|
|
|
if ((new_so_min > old_so_min + 1)); then
|
|
echo "SO minor version for $so_file was incremented more than once. Please revert minor version to $((old_so_min + 1))."
|
|
touch $fail_file
|
|
fi
|
|
|
|
if ((new_so_maj > old_so_maj + 1)); then
|
|
echo "SO major version for $so_file was incremented more than once. Please revert major version to $((old_so_maj + 1))."
|
|
touch $fail_file
|
|
fi
|
|
fi
|
|
|
|
if ((abidiff_output == 1)); then
|
|
"${cmd_args[@]}" --impacted-interfaces || :
|
|
fi
|
|
fi
|
|
processed_so=$((processed_so + 1))
|
|
done
|
|
rm -f $suppression_file
|
|
echo "Processed $processed_so objects."
|
|
}
|
|
|
|
function get_lib_shortname() {
|
|
local lib=${1##*/}
|
|
echo "${lib//@(libspdk_|.so)/}"
|
|
}
|
|
|
|
function import_libs_deps_mk() {
|
|
local var_mk val_mk dep_mk fun_mk
|
|
while read -r var_mk _ val_mk; do
|
|
if [[ $var_mk == "#"* || ! $var_mk =~ (DEPDIRS-|_DEPS|_LIBS) ]]; then
|
|
continue
|
|
fi
|
|
var_mk=${var_mk#*-}
|
|
for dep_mk in $val_mk; do
|
|
fun_mk=${dep_mk//@('$('|')')/}
|
|
if [[ $fun_mk != "$dep_mk" ]]; then
|
|
eval "${fun_mk}() { echo \$$fun_mk ; }"
|
|
# Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf.
|
|
elif ((IGNORED_LIBS["$dep_mk"] == 1)) || [[ $dep_mk =~ event_ ]]; then
|
|
continue
|
|
fi
|
|
eval "$var_mk=\${$var_mk:+\$$var_mk }$dep_mk"
|
|
done
|
|
done < "$libdeps_file"
|
|
}
|
|
|
|
function confirm_deps() {
|
|
local lib=$1 deplib lib_shortname
|
|
|
|
lib_shortname=$(get_lib_shortname "$lib")
|
|
lib_make_deps=(${!lib_shortname})
|
|
|
|
symbols=($(readelf -s --wide "$lib" | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort -u))
|
|
symbols_regx=$(
|
|
IFS="|"
|
|
echo "(${symbols[*]})"
|
|
)
|
|
|
|
if ((${#symbols[@]} > 0)); then
|
|
for deplib in "$libdir/"libspdk_!("$lib_shortname").so; do
|
|
readelf -s --wide "$deplib" | grep -m1 -qE "DEFAULT\s+[0-9]+\s$symbols_regx$" || continue
|
|
found_symbol_lib=$(get_lib_shortname "$deplib")
|
|
# Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it.
|
|
if [[ $found_symbol_lib != *env_dpdk* ]]; then
|
|
dep_names+=("$found_symbol_lib")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
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 "---------------------------------------------------------------------"
|
|
fi
|
|
}
|
|
|
|
function confirm_makefile_deps() {
|
|
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=("$libdir/"libspdk_!(env_dpdk).so)
|
|
|
|
declare -A IGNORED_LIBS=()
|
|
if grep -q 'CONFIG_RDMA?=n' $rootdir/mk/config.mk; then
|
|
IGNORED_LIBS["rdma"]=1
|
|
fi
|
|
|
|
(
|
|
import_libs_deps_mk
|
|
for lib in "${SPDK_LIBS[@]}"; do confirm_deps "$lib" & done
|
|
wait
|
|
)
|
|
}
|
|
|
|
config_params=$(get_config_params)
|
|
if [ "$SPDK_TEST_OCF" -eq 1 ]; then
|
|
config_params="$config_params --with-ocf=$rootdir/build/ocf.a"
|
|
fi
|
|
|
|
$MAKE $MAKEFLAGS clean
|
|
./configure $config_params --with-shared
|
|
# By setting SPDK_NO_LIB_DEPS=1, 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.
|
|
SPDK_NO_LIB_DEPS=1 $MAKE $MAKEFLAGS
|
|
|
|
xtrace_disable
|
|
|
|
fail_file=$output_dir/check_so_deps_fail
|
|
|
|
rm -f $fail_file
|
|
|
|
run_test "confirm_abi_deps" confirm_abi_deps
|
|
|
|
run_test "confirm_makefile_deps" confirm_makefile_deps
|
|
|
|
$MAKE $MAKEFLAGS clean
|
|
|
|
if [ -f $fail_file ]; then
|
|
rm -f $fail_file
|
|
echo "shared object test failed"
|
|
exit 1
|
|
fi
|
|
|
|
xtrace_restore
|