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