xref: /spdk/test/common/autobuild_common.sh (revision c30dfbc2d7c72eef8a655b225b2662977a38b4c0)
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			# Commit https://review.spdk.io/gerrit/c/spdk/dpdk/+/16134 is required for DPDK 22.11+
171			if ge $dpdk_ver 22.11.0; then
172				patch -p1 < "$rootdir/test/common/config/pkgdep/patches/dpdk/22.11+/dpdk_ipsec_mb.patch"
173			fi
174		fi
175	fi
176
177	dpdk_kmods="false"
178	if [ "$(uname -s)" = "FreeBSD" ]; then
179		dpdk_kmods="true"
180	fi
181
182	meson build-tmp --prefix="$external_dpdk_dir" --libdir lib \
183		-Denable_docs=false -Denable_kmods="$dpdk_kmods" -Dtests=false \
184		-Dc_link_args="$dpdk_ldflags" -Dc_args="$dpdk_cflags" \
185		-Dmachine=native -Denable_drivers=$(printf "%s," "${DPDK_DRIVERS[@]}")
186	ninja -C "$external_dpdk_base_dir/build-tmp" $MAKEFLAGS
187	ninja -C "$external_dpdk_base_dir/build-tmp" $MAKEFLAGS install
188
189	# Save this path. In tests are run using autorun.sh then autotest.sh
190	# script will be unaware of LD_LIBRARY_PATH and will fail tests.
191	echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" > /tmp/spdk-ld-path
192
193	cd "$orgdir"
194}
195
196check_dpdk_pci_api() {
197	local dpdk_dir
198
199	if [[ -n "$SPDK_TEST_NATIVE_DPDK" ]]; then
200		dpdk_dir=$(dirname "$SPDK_RUN_EXTERNAL_DPDK")
201	fi
202
203	"$rootdir/scripts/env_dpdk/check_dpdk_pci_api.sh" check "$dpdk_dir"
204}
205
206make_fail_cleanup() {
207	if [ -d $out/scan-build-tmp ]; then
208		scanoutput=$(ls -1 $out/scan-build-tmp/)
209		mv $out/scan-build-tmp/$scanoutput $out/scan-build
210		rm -rf $out/scan-build-tmp
211		chmod -R a+rX $out/scan-build
212	fi
213	false
214}
215
216_scanbuild_make() {
217	pass=true
218	"$rootdir/configure" $config_params --without-shared
219	$scanbuild $MAKE $MAKEFLAGS > $out/build_output.txt && rm -rf $out/scan-build-tmp || make_fail_cleanup
220	xtrace_disable
221
222	rm -f $out/*files.txt
223	for ent in $(find app examples lib module test -type f | grep -vF ".h"); do
224		if [[ $ent == lib/env_ocf* ]]; then continue; fi
225		if file -bi $ent | grep -q 'text/x-c'; then
226			echo $ent | sed 's/\.cp\{0,2\}$//g' >> $out/all_c_files.txt
227		fi
228	done
229	xtrace_restore
230
231	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
232	cat $rootdir/test/common/skipped_build_files.txt >> $out/built_c_files.txt
233
234	sort -o $out/all_c_files.txt $out/all_c_files.txt
235	sort -o $out/built_c_files.txt $out/built_c_files.txt
236	# from comm manual:
237	#   -2 suppress column 2 (lines unique to FILE2)
238	#   -3 suppress column 3 (lines that appear in both files)
239	# comm may exit 1 if no lines were printed (undocumented, unreliable)
240	comm -2 -3 $out/all_c_files.txt $out/built_c_files.txt > $out/unbuilt_c_files.txt || true
241
242	if [ $(wc -l < $out/unbuilt_c_files.txt) -ge 1 ]; then
243		echo "missing files"
244		cat <<- ERROR
245			The following C files were not built.  Either scanbuild CI job needs to
246			be updated with proper flags to build these files, or exceptions need
247			to be added to test/common/skipped_build_files.txt
248
249			$(<"$out/unbuilt_c_files.txt")
250		ERROR
251		pass=false
252	fi
253
254	$pass
255}
256
257porcelain_check() {
258	if [ $(git status --porcelain --ignore-submodules | wc -l) -ne 0 ]; then
259		echo "Generated files missing from .gitignore:"
260		git status --porcelain --ignore-submodules
261		exit 1
262	fi
263}
264
265# Check that header file dependencies are working correctly by
266#  capturing a binary's stat data before and after touching a
267#  header file and re-making.
268header_dependency_check() {
269	STAT1=$(stat $SPDK_BIN_DIR/spdk_tgt)
270	sleep 1
271	touch "$rootdir/lib/nvme/nvme_internal.h"
272	$MAKE $MAKEFLAGS
273	STAT2=$(stat $SPDK_BIN_DIR/spdk_tgt)
274
275	if [ "$STAT1" == "$STAT2" ]; then
276		echo "Header dependency check failed"
277		false
278	fi
279}
280
281test_make_install() {
282	$MAKE $MAKEFLAGS install DESTDIR="$SPDK_WORKSPACE" prefix=/usr
283}
284
285test_make_uninstall() {
286	# Create empty file to check if it is not deleted by target uninstall
287	touch "$SPDK_WORKSPACE/usr/lib/sample_xyz.a"
288	$MAKE $MAKEFLAGS uninstall DESTDIR="$SPDK_WORKSPACE" prefix=/usr
289	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
290		ls -lR "$SPDK_WORKSPACE"
291		echo "Make uninstall failed"
292		exit 1
293	fi
294}
295
296_build_doc() {
297	local doxygenv
298	doxygenv=$(doxygen --version)
299
300	$MAKE -C "$rootdir"/doc --no-print-directory $MAKEFLAGS &> "$out"/doxygen.log
301	if [ -s "$out"/doxygen.log ]; then
302		if [[ "$doxygenv" == "1.8.20" ]]; then
303			# Doxygen 1.8.20 produces false positives, see:
304			# https://github.com/doxygen/doxygen/issues/7948
305			grep -vE '\\ilinebr'
306		elif [[ "$doxygenv" == "1.9.5" ]]; then
307			# Doxygen 1.9.5 produces false positives, see:
308			# https://github.com/doxygen/doxygen/issues/9552 and
309			# https://github.com/doxygen/doxygen/issues/9678
310			grep -vE '\\ifile|@param'
311		fi < "$out/doxygen.log" && echo "Doxygen errors found!" && return 1
312
313		echo "Doxygen $doxygenv detected. No warnings except false positives, continuing the test"
314	fi
315	if hash pdflatex 2> /dev/null; then
316		$MAKE -C "$rootdir"/doc/output/latex --no-print-directory $MAKEFLAGS &>> "$out"/doxygen.log
317	fi
318	mkdir -p "$out"/doc
319	# Copy and remove files to avoid mv: failed to preserve ownership error
320	cp -r --preserve=mode "$rootdir"/doc/output/html "$out"/doc
321	rm -rf "$rootdir"/doc/output/html
322	if [ -f "$rootdir"/doc/output/latex/refman.pdf ]; then
323		mv "$rootdir"/doc/output/latex/refman.pdf "$out"/doc/spdk.pdf
324	fi
325	$MAKE -C "$rootdir"/doc --no-print-directory $MAKEFLAGS clean &>> "$out"/doxygen.log
326	if [ -s "$out"/doxygen.log ]; then
327		# Save the log as an artifact in case we are working with potentially broken version
328		eq "$doxygenv" 1.8.20 || rm "$out"/doxygen.log
329	fi
330	rm -rf "$rootdir"/doc/output
331}
332
333check_format() {
334	run_test "autobuild_check_format" "$rootdir/scripts/check_format.sh"
335}
336
337check_so_deps() {
338	run_test "autobuild_check_so_deps" "$rootdir/test/make/check_so_deps.sh" "$spdk_conf"
339}
340
341external_code() {
342	run_test "autobuild_external_code" "$rootdir/test/external_code/test_make.sh" "$rootdir"
343}
344
345dpdk_pci_api() {
346	run_test "autobuild_check_dpdk_pci_api" check_dpdk_pci_api
347}
348
349build_files() {
350	"$rootdir/configure" $config_params --without-shared
351	$MAKE $MAKEFLAGS
352	run_test "autobuild_generated_files_check" porcelain_check
353	run_test "autobuild_header_dependency_check" header_dependency_check
354	run_test "autobuild_make_install" test_make_install
355	run_test "autobuild_make_uninstall" test_make_uninstall
356}
357
358build_doc() {
359	"$rootdir/configure" $config_params --without-shared
360	run_test "autobuild_build_doc" _build_doc
361}
362
363autobuild_test_suite_tiny() {
364	check_format
365	check_so_deps
366	dpdk_pci_api
367}
368
369autobuild_test_suite_ext() {
370	external_code
371}
372
373autobuild_test_suite_full() {
374	autobuild_test_suite_tiny
375	autobuild_test_suite_ext
376	build_files
377	build_doc
378}
379
380_autobuild_test_suite() {
381	case "$SPDK_TEST_AUTOBUILD" in
382		tiny) autobuild_test_suite_tiny ;;
383		ext) autobuild_test_suite_ext ;;
384		full) autobuild_test_suite_full ;;
385	esac
386}
387
388_unittest_build() {
389	"$rootdir/configure" $config_params --without-shared
390	$MAKE $MAKEFLAGS
391}
392
393autobuild_test_suite() {
394	run_test "autobuild" _autobuild_test_suite
395}
396
397unittest_build() {
398	run_test "unittest_build" _unittest_build
399}
400
401scanbuild_make() {
402	run_test "scanbuild_make" _scanbuild_make
403}
404
405ocf_precompile() {
406	run_test "autobuild_ocf_precompile" _ocf_precompile
407}
408
409llvm_precompile() {
410	run_test "autobuild_llvm_precompile" _llvm_precompile
411}
412
413build_native_dpdk() {
414	run_test "build_native_dpdk" _build_native_dpdk
415}
416
417out=$output_dir
418SPDK_WORKSPACE=$(mktemp -dt "spdk_$(date +%s).XXXXXX")
419
420if [[ -n $EXTERNAL_MAKE_HUGEMEM ]]; then
421	export EXTERNAL_MAKE_HUGEMEM
422fi
423
424if [ -n "$SPDK_TEST_NATIVE_DPDK" ]; then
425	scanbuild_exclude=" --exclude $(dirname $SPDK_RUN_EXTERNAL_DPDK)"
426else
427	scanbuild_exclude="--exclude $rootdir/dpdk/"
428fi
429# We exclude /tmp as it's used by xnvme's liburing subproject for storing
430# temporary .c files which are picked up as buggy by the scanbuild.
431scanbuild_exclude+=" --exclude $rootdir/xnvme --exclude /tmp"
432
433scanbuild="scan-build -o $output_dir/scan-build-tmp $scanbuild_exclude --status-bugs"
434config_params=$(get_config_params)
435
436spdk_conf=${spdk_conf:-"$1"}
437
438if [[ ! -f $spdk_conf ]]; then
439	echo "ERROR: SPDK test configuration not specified"
440	return 1
441fi
442