1#!/usr/bin/ksh
2#
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13
14#
15# Copyright (c) 2019, Joyent, Inc.
16#
17
18#
19# Run all of the various CTF tests
20#
21
22unalias -a
23#set -o xtrace
24
25if [[ -z "$TMPDIR" ]]; then
26	TMPDIR="/tmp"
27fi
28
29
30ctf_arg0=$(basename $0)
31ctf_root=$(cd $(dirname $0) && echo $PWD)
32ctf_tests=
33ctf_cc="gcc"
34ctf_cxx="g++"
35ctf_as="gas"
36ctf_convert="ctfconvert"
37ctf_merge="ctfmerge"
38ctf_debugflags="-gdwarf-2 "
39ctf_mach32="-m32"
40ctf_mach64="-m64"
41ctf_temp="$TMPDIR/ctftest.$$.o"
42ctf_makefile="Makefile.ctftest"
43ctf_nerrs=0
44ctf_cc_type=
45ctf_cc_vers=
46
47usage()
48{
49	typeset msg="$*"
50	[[ -z "$msg" ]] || echo "$msg" >&2
51	cat <<USAGE >&2
52Usage: $ctf_arg0 [-a as] [-c cc] [-C CC] [-g flags] [-m ctfmerge] [-t ctfconvert]
53
54	Runs the CTF test suite
55
56	-a assembler		Use the specified assembler, defaults to 'as'
57	-c compiler		Use the specified C compiler, defaults to 'gcc'
58	-C compiler		Use the specified C++ compiler, defaults to 'g++'
59	-g flags		Use flags to generate debug info. Defaults to
60				"-gdwarf-2".
61	-m ctfmerge		Use the specified ctfmerge, defaults to
62				'ctfmerge'
63	-t ctfconvert		Use the specified ctfconvert, defaults to
64				'ctfconvert'
65USAGE
66	exit 2
67}
68
69
70test_fail()
71{
72	typeset msg="$*"
73	[[ -z "$msg" ]] && msg="failed"
74	echo "TEST FAILED: $msg" >&2
75	((ctf_nerrs++))
76}
77
78warn()
79{
80	typeset msg="$*"
81	echo "$ctf_arg0: $msg" >&2
82}
83
84fatal()
85{
86	typeset msg="$*"
87	[[ -z "$msg" ]] && msg="failed"
88	echo "$ctf_arg0: $msg" >&2
89	rm -f "$ctf_temp"
90	exit 1
91}
92
93#
94# Attempt to try and figure out what class and version of compiler we
95# are dealing with so we can try and skip known failures due to existing
96# bugs in the compiler.
97#
98determine_compiler()
99{
100	typeset name=$($ctf_cc --version | awk '{ print $1; exit; }')
101	typeset version
102
103	if [[ "$name" == "gcc" ]]; then
104		version=$($ctf_cc --version | awk '{ print $NF; exit; }')
105	elif [[ "$name" == "clang" ]]; then
106		version=$($ctf_cc --version | awk '{ print $NF; exit; }')
107	else
108		warn "failed to parse compiler name from $ctf_cc, will " \
109		    "not make any assumptions about expected failures"
110		name="unknown"
111		version="unknown"
112	fi
113
114	ctf_cc_type="$name"
115	ctf_cc_version="$version"
116}
117
118announce()
119{
120	cat << EOF
121Beginning CTF tests with the following settings:
122cc:		$(which $ctf_cc)
123detected:	$ctf_cc_type $ctf_cc_version
124CC:		$(which $ctf_cxx)
125as:		$(which $ctf_as)
126ctfconvert:	$(which $ctf_convert)
127ctfmerge:	$(which $ctf_merge)
12832-bit CFLAGS:	$ctf_32cflags
12964-bit CFLAGS:	$ctf_64cflags
130
131EOF
132}
133
134run_one()
135{
136	typeset source=$1 checker=$2 flags=$3
137
138	if ! "$ctf_cc" $flags -o "$ctf_temp" -c "$source"; then
139		test_fail "failed to compile $source with flags: $flags"
140		return
141	fi
142
143	if ! "$ctf_convert" "$ctf_temp"; then
144		test_fail "failed to convert CTF in $source"
145		return
146	fi
147
148	if ! "$checker" "$ctf_temp"; then
149		test_fail "check for $source, $checker, failed"
150		return
151	fi
152
153	rm -f "$ctf_temp"
154	echo "TEST PASSED: $source $flags"
155}
156
157#
158# Perform a more complex build. The Makefile present will drive the
159# building of the artifacts and the running of the tests based on the
160# variables that we pass to it.
161#
162run_dir()
163{
164	typeset dir outdir check32 check64 flags32 flags64
165
166	dir=$1
167	outdir="$TMPDIR/ctftest.$$-$(basename $d)"
168	check32=$2
169	flags32=$3
170	check64=$4
171	flags64=$5
172
173	if ! mkdir $outdir; then
174		fatal "failed to make temporary directory '$outdir'"
175	fi
176
177	if ! make -C $dir -f Makefile.ctftest \
178	    BUILDDIR="$outdir" \
179	    CC="$ctf_cc" \
180	    CFLAGS32="$ctf_mach32" \
181	    CFLAGS64="$ctf_mach64" \
182	    DEBUGFLAGS="$ctf_debugflags" \
183	    CTFCONVERT="$ctf_convert" \
184	    CTFMERGE="$ctf_merge" \
185	    build 1>/dev/null; then
186		rm -rf $outdir
187		test_fail "failed to build $dir"
188		return
189	fi
190
191	if ! make -C $dir -f Makefile.ctftest \
192	    BUILDDIR="$outdir" \
193	    CHECK32="$check32" \
194	    CHECK64="$check64" \
195	    run-test 1>/dev/null; then
196		rm -rf $outdir
197		test_fail "failed to run tests for $dir"
198		return
199	fi
200
201	rm -rf $outdir
202	echo "TEST PASSED: $dir (dir)"
203}
204
205#
206# Find all of the tests that exist and then try to run them all. Tests
207# may either be a single file or a directory.
208#
209run_tests()
210{
211	typeset t base check
212	ctf_tests=$(ls "$ctf_root"/*.c)
213	for t in $ctf_tests; do
214		base=$(basename "$t" .c)
215		check=$(echo "$base" | sed s/test-/check-/)
216		if [[ -f "$ctf_root/$check" ]]; then
217			run_one $t "$ctf_root/$check" "$ctf_32cflags"
218			run_one $t "$ctf_root/$check" "$ctf_64cflags"
219		elif [[ -f "$ctf_root/$check-32" && \
220		    -f "$ctf_root/$check-64" ]]; then
221			run_one $t "$ctf_root/$check-32" "$ctf_32cflags"
222			run_one $t "$ctf_root/$check-64" "$ctf_64cflags"
223		else
224			test_fail "missing checker for $t"
225		fi
226	done
227
228	for d in $(find "$ctf_root" -maxdepth 1 -type d -name 'test-*'); do
229		[[ ! -f "$d/$ctf_makefile" ]] && continue
230		base=$(basename "$d")
231		check=$(echo "$base" | sed s/test-/check-/)
232		if [[ -f "$ctf_root/$check" ]]; then
233			run_dir $d "$ctf_root/$check" "$ctf_32cflags" \
234			    "$ctf_root/$check" "$ctf_64cflags"
235		elif [[ -f "$ctf_root/$check-32" && \
236		    -f "$ctf_root/$check-64" ]]; then
237			run_dir $d "$ctf_root/$check-32" "$ctf_32cflags" \
238			    "$ctf_root/$check-64" "$ctf_64cflags"
239		else
240			test_fail "missing checker for $t"
241		fi
242	done
243
244	outdir="$TMPDIR/ctftest.$$"
245
246	for f in $(find "$ctf_root" -maxdepth 1 -type f -name 'ctftest-*'); do
247		if ! mkdir $outdir; then
248			fatal "failed to make temporary directory '$outdir'"
249		fi
250
251		echo "Running $f in $outdir"
252
253		(cd $outdir && $f)
254
255		if [[ $? -ne 0 ]]; then
256			test_fail "$f failed"
257		else
258			echo "TEST PASSED: $f"
259		fi
260
261		rm -rf $outdir
262	done
263}
264
265while getopts ":a:C:c:g:m:t:" c $@; do
266	case "$c" in
267	a)
268		ctf_as=$OPTARG
269		;;
270	C)
271		ctf_cxx=$OPTARG
272		;;
273	c)
274		ctf_cc=$OPTARG
275		;;
276	g)
277		ctf_debugflags=$OPTARG
278		;;
279	m)
280		ctf_merge=$OPTARG
281		;;
282	t)
283		ctf_convert=$OPTARG
284		;;
285	:)
286		usage "option requires an argument -- $OPTARG"
287		;;
288	*)
289		usage "invalid option -- $OPTARG"
290		;;
291	esac
292done
293
294ctf_32cflags="$ctf_mach32 $ctf_debugflags"
295ctf_64cflags="$ctf_mach64 $ctf_debugflags"
296
297determine_compiler
298
299export ctf_as ctf_cc ctf_cxx ctf_debugflags ctf_merge ctf_convert
300export ctf_cc_type ctf_cc_version
301
302announce
303
304run_tests
305
306if [[ $ctf_nerrs -ne 0 ]]; then
307	if [[ $ctf_nerrs -eq 1 ]]; then
308		printf "\n%s: %u test failed\n" "$ctf_arg0" "$ctf_nerrs"
309	else
310		printf "\n%s: %u tests failed\n" "$ctf_arg0" "$ctf_nerrs"
311	fi
312	exit 1
313else
314	printf "\n%s: All tests passed successfully\n" "$ctf_arg0"
315	exit 0
316fi
317