xref: /spdk/test/common/autobuild_common.sh (revision f6866117acb32c78d5ea7bd76ba330284655af35)
1#!/usr/bin/env bash
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright (C) 2022 Intel Corporation.
4# All rights reserved.
5
6source "$rootdir/test/common/autotest_common.sh"
7source "$rootdir/scripts/common.sh"
8
9_ocf_precompile() {
10	# We compile OCF sources ourselves
11	# They don't need to be checked with scanbuild and code coverage is not applicable
12	# So we precompile OCF now for further use as standalone static library
13	"$rootdir/configure" $(echo $config_params | sed 's/--enable-coverage//g')
14	$MAKE $MAKEFLAGS include/spdk/config.h
15	CC=gcc CCAR=ar $MAKE $MAKEFLAGS -C "$rootdir/lib/env_ocf" exportlib O="$rootdir/ocf.a"
16	# Set config to use precompiled library
17	config_params="$config_params --with-ocf=/$rootdir/ocf.a"
18	# need to reconfigure to avoid clearing ocf related files on future make clean.
19	"$rootdir/configure" $config_params
20}
21
22# Find matching llvm fuzzer library and clang compiler version
23_llvm_precompile() {
24	[[ $(clang --version) =~ "version "(([0-9]+).([0-9]+).([0-9]+)) ]]
25	clang_version=${BASH_REMATCH[1]}
26	clang_num=${BASH_REMATCH[2]}
27
28	export CC=clang-$clang_num
29	export CXX=clang++-$clang_num
30
31	fuzzer_libs=(/usr/lib*/clang/"$clang_version"/lib/linux/libclang_rt.fuzzer_no_main-x86_64.a)
32	fuzzer_lib=${fuzzer_libs[0]}
33	[[ -e $fuzzer_lib ]]
34
35	config_params="$config_params --with-fuzzer=$fuzzer_lib"
36	# need to reconfigure to avoid clearing llvm related files on future make clean.
37	"$rootdir/configure" $config_params
38}
39
40_build_native_dpdk() {
41	local external_dpdk_dir
42	local external_dpdk_base_dir
43	local compiler_version
44	local compiler
45	local dpdk_kmods
46	local repo='dpdk'
47
48	compiler=${CC:-gcc}
49
50	# Export CC to be absolutely sure it's set.
51	# If CC was not set and we defaulted to "gcc" then we need to do the export
52	# so that "meson build" command a few lines below is aware of which compiler
53	# to use.
54	export CC="$compiler"
55
56	if [[ $compiler != *clang* && $compiler != *gcc* ]]; then
57		echo "Unsupported compiler detected ($compiler), failing the test" >&2
58		return 1
59	fi
60
61	if [[ $SPDK_TEST_NATIVE_DPDK != 'main' ]]; then
62		repo='dpdk-stable'
63	fi
64
65	compiler_version=$("$compiler" -dumpversion)
66	compiler_version=${compiler_version%%.*}
67	external_dpdk_dir="$SPDK_RUN_EXTERNAL_DPDK"
68	external_dpdk_base_dir="$(dirname $external_dpdk_dir)"
69
70	if [[ ! -d "$external_dpdk_base_dir" ]]; then
71		sudo mkdir -p "$external_dpdk_base_dir"
72		sudo chown -R $(whoami) "$external_dpdk_base_dir"/..
73	fi
74	orgdir=$PWD
75
76	rm -rf "$external_dpdk_base_dir"
77	git clone --branch $SPDK_TEST_NATIVE_DPDK --depth 1 http://dpdk.org/git/${repo} "$external_dpdk_base_dir"
78	git -C "$external_dpdk_base_dir" log --oneline -n 5
79
80	dpdk_cflags="-fPIC -g -fcommon"
81	dpdk_ldflags=""
82	dpdk_ver=$(< "$external_dpdk_base_dir/VERSION")
83
84	if [[ $compiler == *gcc* && $compiler_version -ge 5 ]]; then
85		dpdk_cflags+=" -Werror"
86	fi
87
88	if [[ $compiler == *gcc* && $compiler_version -ge 10 ]]; then
89		dpdk_cflags+=" -Wno-stringop-overflow"
90	fi
91
92	# the drivers we use
93	# net/i40e driver is not really needed by us, but it's built as a workaround
94	# for DPDK issue: https://bugs.dpdk.org/show_bug.cgi?id=576
95	DPDK_DRIVERS=("bus" "bus/pci" "bus/vdev" "mempool/ring" "net/i40e" "net/i40e/base")
96
97	local mlx5_libs_added="n"
98	if [[ "$SPDK_TEST_CRYPTO" -eq 1 || "$SPDK_TEST_SMA" -eq 1 ]]; then
99		intel_ipsec_mb_ver=v0.54
100		intel_ipsec_mb_drv=crypto/aesni_mb
101		intel_ipsec_lib=""
102		if ge "$dpdk_ver" 21.11.0; then
103			# Minimum supported version of intel-ipsec-mb, for DPDK >= 21.11, is 1.0.
104			# Source of the aesni_mb driver was moved to ipsec_mb. .{h,so,a} were moved
105			# to ./lib.
106			# https://github.com/dpdk/dpdk/commit/918fd2f1466b0e3b21a033df7012a77a83665582.
107			intel_ipsec_mb_ver=v1.0
108			intel_ipsec_mb_drv=crypto/ipsec_mb
109			intel_ipsec_lib=lib
110		fi
111		git clone --branch "$intel_ipsec_mb_ver" --depth 1 https://github.com/intel/intel-ipsec-mb.git "$external_dpdk_base_dir/intel-ipsec-mb"
112		cd "$external_dpdk_base_dir/intel-ipsec-mb"
113		$MAKE $MAKEFLAGS all SHARED=y EXTRA_CFLAGS=-fPIC
114		DPDK_DRIVERS+=("crypto")
115		DPDK_DRIVERS+=("$intel_ipsec_mb_drv")
116		DPDK_DRIVERS+=("crypto/qat")
117		DPDK_DRIVERS+=("compress/qat")
118		DPDK_DRIVERS+=("common/qat")
119		# 21.11.0 is version of DPDK with stable support for mlx5 crypto.
120		if ge "$dpdk_ver" 21.11.0; then
121			# SPDK enables CRYPTO_MLX in case supported version of DPDK is detected
122			# so make sure proper libs are built.
123			DPDK_DRIVERS+=("bus/auxiliary")
124			DPDK_DRIVERS+=("common/mlx5")
125			DPDK_DRIVERS+=("common/mlx5/linux")
126			DPDK_DRIVERS+=("crypto/mlx5")
127			mlx5_libs_added="y"
128		fi
129		dpdk_cflags+=" -I$external_dpdk_base_dir/intel-ipsec-mb/$intel_ipsec_lib"
130		dpdk_ldflags+=" -L$external_dpdk_base_dir/intel-ipsec-mb/$intel_ipsec_lib"
131		export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$external_dpdk_base_dir/intel-ipsec-mb/$intel_ipsec_lib"
132	fi
133
134	if [[ "$SPDK_TEST_VBDEV_COMPRESS" -eq 1 ]]; then
135		isal_dir="$external_dpdk_base_dir/isa-l"
136		git clone --branch v2.29.0 --depth 1 https://github.com/intel/isa-l.git "$isal_dir"
137
138		cd $isal_dir
139		./autogen.sh
140		./configure CFLAGS="-fPIC -g -O2" --enable-shared=yes --prefix="$isal_dir/build"
141		ln -s $PWD/include $PWD/isa-l
142		$MAKE $MAKEFLAGS all
143		$MAKE install
144		DPDK_DRIVERS+=("compress")
145		DPDK_DRIVERS+=("compress/isal")
146		DPDK_DRIVERS+=("compress/qat")
147		DPDK_DRIVERS+=("common/qat")
148		if ge "$dpdk_ver" 21.02.0; then
149			# SPDK enables REDUCE_MLX in case supported version of DPDK is detected
150			# so make sure proper libs are built.
151			if test $mlx5_libs_added = "n"; then
152				DPDK_DRIVERS+=("bus/auxiliary")
153				DPDK_DRIVERS+=("common/mlx5")
154				DPDK_DRIVERS+=("common/mlx5/linux")
155			fi
156			DPDK_DRIVERS+=("compress/mlx5")
157		fi
158		export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$isal_dir/build/lib/pkgconfig"
159		export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$isal_dir/build/lib"
160	fi
161
162	cd $external_dpdk_base_dir
163	if [ "$(uname -s)" = "Linux" ]; then
164		if lt $dpdk_ver 21.11.0; then
165			patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/20.11/dpdk_pci.patch"
166			patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/20.11/dpdk_qat.patch"
167		else
168			patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/21.11+/dpdk_qat.patch"
169
170			if lt $dpdk_ver 23.03.0; then
171				# Commit https://review.spdk.io/gerrit/c/spdk/dpdk/+/16828 is required for DPDK <23.03.0
172				patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/21.11+/dpdk_rte_thash_gfni.patch"
173			fi
174
175			# Commit https://review.spdk.io/gerrit/c/spdk/dpdk/+/16134 is required for DPDK 22.11+
176			if ge $dpdk_ver 22.11.0; then
177				patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/22.11+/dpdk_ipsec_mb.patch"
178			fi
179		fi
180	fi
181
182	dpdk_kmods="false"
183	if [ "$(uname -s)" = "FreeBSD" ]; then
184		dpdk_kmods="true"
185	fi
186
187	meson build-tmp --prefix="$external_dpdk_dir" --libdir lib \
188		-Denable_docs=false -Denable_kmods="$dpdk_kmods" -Dtests=false \
189		-Dc_link_args="$dpdk_ldflags" -Dc_args="$dpdk_cflags" \
190		-Dmachine=native -Denable_drivers=$(printf "%s," "${DPDK_DRIVERS[@]}")
191	ninja -C "$external_dpdk_base_dir/build-tmp" $MAKEFLAGS
192	ninja -C "$external_dpdk_base_dir/build-tmp" $MAKEFLAGS install
193
194	# Save this path. In tests are run using autorun.sh then autotest.sh
195	# script will be unaware of LD_LIBRARY_PATH and will fail tests.
196	echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" > /tmp/spdk-ld-path
197
198	cd "$orgdir"
199}
200
201check_dpdk_pci_api() {
202	local dpdk_dir
203
204	if [[ -n "$SPDK_TEST_NATIVE_DPDK" ]]; then
205		dpdk_dir=$(dirname "$SPDK_RUN_EXTERNAL_DPDK")
206	fi
207
208	"$rootdir/scripts/env_dpdk/check_dpdk_pci_api.sh" check "$dpdk_dir"
209}
210
211make_fail_cleanup() {
212	if [ -d $out/scan-build-tmp ]; then
213		scanoutput=$(ls -1 $out/scan-build-tmp/)
214		mv $out/scan-build-tmp/$scanoutput $out/scan-build
215		rm -rf $out/scan-build-tmp
216		chmod -R a+rX $out/scan-build
217	fi
218	false
219}
220
221_scanbuild_make() {
222	pass=true
223	"$rootdir/configure" $config_params --without-shared
224	$scanbuild $MAKE $MAKEFLAGS > $out/build_output.txt && rm -rf $out/scan-build-tmp || make_fail_cleanup
225	xtrace_disable
226
227	rm -f $out/*files.txt
228	for ent in $(find app examples lib module test -type f | grep -vF ".h"); do
229		if [[ $ent == lib/env_ocf* ]]; then continue; fi
230		if file -bi $ent | grep -q 'text/x-c'; then
231			echo $ent | sed 's/\.cp\{0,2\}$//g' >> $out/all_c_files.txt
232		fi
233	done
234	xtrace_restore
235
236	grep -E "CC|CXX" $out/build_output.txt | sed 's/\s\s\(CC\|CXX\)\s//g' | sed 's/\.o//g' > $out/built_c_files.txt
237	cat $rootdir/test/common/skipped_build_files.txt >> $out/built_c_files.txt
238
239	sort -o $out/all_c_files.txt $out/all_c_files.txt
240	sort -o $out/built_c_files.txt $out/built_c_files.txt
241	# from comm manual:
242	#   -2 suppress column 2 (lines unique to FILE2)
243	#   -3 suppress column 3 (lines that appear in both files)
244	# comm may exit 1 if no lines were printed (undocumented, unreliable)
245	comm -2 -3 $out/all_c_files.txt $out/built_c_files.txt > $out/unbuilt_c_files.txt || true
246
247	if [ $(wc -l < $out/unbuilt_c_files.txt) -ge 1 ]; then
248		echo "missing files"
249		cat <<- ERROR
250			The following C files were not built.  Either scanbuild CI job needs to
251			be updated with proper flags to build these files, or exceptions need
252			to be added to test/common/skipped_build_files.txt
253
254			$(<"$out/unbuilt_c_files.txt")
255		ERROR
256		pass=false
257	fi
258
259	$pass
260}
261
262porcelain_check() {
263	if [ $(git status --porcelain --ignore-submodules | wc -l) -ne 0 ]; then
264		echo "Generated files missing from .gitignore:"
265		git status --porcelain --ignore-submodules
266		exit 1
267	fi
268}
269
270# Check that header file dependencies are working correctly by
271#  capturing a binary's stat data before and after touching a
272#  header file and re-making.
273header_dependency_check() {
274	STAT1=$(stat $SPDK_BIN_DIR/spdk_tgt)
275	sleep 1
276	touch "$rootdir/lib/nvme/nvme_internal.h"
277	$MAKE $MAKEFLAGS
278	STAT2=$(stat $SPDK_BIN_DIR/spdk_tgt)
279
280	if [ "$STAT1" == "$STAT2" ]; then
281		echo "Header dependency check failed"
282		false
283	fi
284}
285
286test_make_install() {
287	$MAKE $MAKEFLAGS install DESTDIR="$SPDK_WORKSPACE" prefix=/usr
288}
289
290test_make_uninstall() {
291	# Create empty file to check if it is not deleted by target uninstall
292	touch "$SPDK_WORKSPACE/usr/lib/sample_xyz.a"
293	$MAKE $MAKEFLAGS uninstall DESTDIR="$SPDK_WORKSPACE" prefix=/usr
294	if [[ $(find "$SPDK_WORKSPACE/usr" -maxdepth 1 -mindepth 1 | wc -l) -ne 2 ]] || [[ $(find "$SPDK_WORKSPACE/usr/lib/" -maxdepth 1 -mindepth 1 | wc -l) -ne 1 ]]; then
295		ls -lR "$SPDK_WORKSPACE"
296		echo "Make uninstall failed"
297		exit 1
298	fi
299}
300
301_build_doc() {
302	local doxygenv
303	doxygenv=$(doxygen --version)
304
305	$MAKE -C "$rootdir"/doc --no-print-directory $MAKEFLAGS &> "$out"/doxygen.log
306	if [ -s "$out"/doxygen.log ]; then
307		if [[ "$doxygenv" == "1.8.20" ]]; then
308			# Doxygen 1.8.20 produces false positives, see:
309			# https://github.com/doxygen/doxygen/issues/7948
310			grep -vE '\\ilinebr'
311		elif [[ "$doxygenv" == "1.9.5" ]]; then
312			# Doxygen 1.9.5 produces false positives, see:
313			# https://github.com/doxygen/doxygen/issues/9552 and
314			# https://github.com/doxygen/doxygen/issues/9678
315			grep -vE '\\ifile|@param'
316		fi < "$out/doxygen.log" && echo "Doxygen errors found!" && return 1
317
318		echo "Doxygen $doxygenv detected. No warnings except false positives, continuing the test"
319	fi
320	if hash pdflatex 2> /dev/null; then
321		$MAKE -C "$rootdir"/doc/output/latex --no-print-directory $MAKEFLAGS &>> "$out"/doxygen.log
322	fi
323	mkdir -p "$out"/doc
324	# Copy and remove files to avoid mv: failed to preserve ownership error
325	cp -r --preserve=mode "$rootdir"/doc/output/html "$out"/doc
326	rm -rf "$rootdir"/doc/output/html
327	if [ -f "$rootdir"/doc/output/latex/refman.pdf ]; then
328		mv "$rootdir"/doc/output/latex/refman.pdf "$out"/doc/spdk.pdf
329	fi
330	$MAKE -C "$rootdir"/doc --no-print-directory $MAKEFLAGS clean &>> "$out"/doxygen.log
331	if [ -s "$out"/doxygen.log ]; then
332		# Save the log as an artifact in case we are working with potentially broken version
333		eq "$doxygenv" 1.8.20 || rm "$out"/doxygen.log
334	fi
335	rm -rf "$rootdir"/doc/output
336}
337
338check_format() {
339	run_test "autobuild_check_format" "$rootdir/scripts/check_format.sh"
340}
341
342check_so_deps() {
343	run_test "autobuild_check_so_deps" "$rootdir/test/make/check_so_deps.sh" "$spdk_conf"
344}
345
346external_code() {
347	run_test "autobuild_external_code" "$rootdir/test/external_code/test_make.sh" "$rootdir"
348}
349
350dpdk_pci_api() {
351	run_test "autobuild_check_dpdk_pci_api" check_dpdk_pci_api
352}
353
354build_files() {
355	"$rootdir/configure" $config_params --without-shared
356	$MAKE $MAKEFLAGS
357	run_test "autobuild_generated_files_check" porcelain_check
358	run_test "autobuild_header_dependency_check" header_dependency_check
359	run_test "autobuild_make_install" test_make_install
360	run_test "autobuild_make_uninstall" test_make_uninstall
361}
362
363build_doc() {
364	"$rootdir/configure" $config_params --without-shared
365	run_test "autobuild_build_doc" _build_doc
366}
367
368autobuild_test_suite_tiny() {
369	check_format
370	check_so_deps
371	dpdk_pci_api
372}
373
374autobuild_test_suite_ext() {
375	external_code
376}
377
378autobuild_test_suite_full() {
379	autobuild_test_suite_tiny
380	autobuild_test_suite_ext
381	build_files
382	build_doc
383}
384
385_autobuild_test_suite() {
386	case "$SPDK_TEST_AUTOBUILD" in
387		tiny) autobuild_test_suite_tiny ;;
388		ext) autobuild_test_suite_ext ;;
389		full) autobuild_test_suite_full ;;
390	esac
391}
392
393_unittest_build() {
394	"$rootdir/configure" $config_params --without-shared
395	$MAKE $MAKEFLAGS
396}
397
398autobuild_test_suite() {
399	run_test "autobuild" _autobuild_test_suite
400}
401
402unittest_build() {
403	run_test "unittest_build" _unittest_build
404}
405
406scanbuild_make() {
407	run_test "scanbuild_make" _scanbuild_make
408}
409
410ocf_precompile() {
411	run_test "autobuild_ocf_precompile" _ocf_precompile
412}
413
414llvm_precompile() {
415	run_test "autobuild_llvm_precompile" _llvm_precompile
416}
417
418build_native_dpdk() {
419	run_test "build_native_dpdk" _build_native_dpdk
420}
421
422out=$output_dir
423SPDK_WORKSPACE=$(mktemp -dt "spdk_$(date +%s).XXXXXX")
424
425if [[ -n $EXTERNAL_MAKE_HUGEMEM ]]; then
426	export EXTERNAL_MAKE_HUGEMEM
427fi
428
429if [ -n "$SPDK_TEST_NATIVE_DPDK" ]; then
430	scanbuild_exclude=" --exclude $(dirname $SPDK_RUN_EXTERNAL_DPDK)"
431else
432	scanbuild_exclude="--exclude $rootdir/dpdk/"
433fi
434# We exclude /tmp as it's used by xnvme's liburing subproject for storing
435# temporary .c files which are picked up as buggy by the scanbuild.
436scanbuild_exclude+=" --exclude $rootdir/xnvme --exclude /tmp"
437
438scanbuild="scan-build -o $output_dir/scan-build-tmp $scanbuild_exclude --status-bugs"
439config_params=$(get_config_params)
440
441spdk_conf=${spdk_conf:-"$1"}
442
443if [[ ! -f $spdk_conf ]]; then
444	echo "ERROR: SPDK test configuration not specified"
445	return 1
446fi
447