1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Test runner for nolibc tests
5
6set -e
7
8trap 'echo Aborting...' 'ERR'
9
10crosstool_version=13.2.0
11hostarch=x86_64
12nproc=$(( $(nproc) + 2))
13cache_dir="${XDG_CACHE_HOME:-"$HOME"/.cache}"
14download_location="${cache_dir}/crosstools/"
15build_location="$(realpath "${cache_dir}"/nolibc-tests/)"
16perform_download=0
17test_mode=system
18werror=1
19llvm=
20all_archs=(
21	i386 x86_64 x32
22	arm64 arm armthumb
23	mips32le mips32be mipsn32le mipsn32be mips64le mips64be
24	ppc ppc64 ppc64le
25	riscv32 riscv64
26	s390x s390
27	loongarch
28	sparc32 sparc64
29	m68k
30	sh4
31)
32archs="${all_archs[@]}"
33
34TEMP=$(getopt -o 'j:d:c:b:a:m:pelh' -n "$0" -- "$@")
35
36eval set -- "$TEMP"
37unset TEMP
38
39print_usage() {
40	cat <<EOF
41Run nolibc testsuite for multiple architectures with crosstools
42
43Usage:
44 $0 [options] <architectures>
45
46Known architectures:
47 ${archs}
48
49Options:
50 -j [N]         Allow N jobs at once (default: ${nproc})
51 -p             Allow download of toolchains
52 -d [DIR]       Download location for toolchains (default: ${download_location})
53 -c [VERSION]   Version of toolchains to use (default: ${crosstool_version})
54 -a [ARCH]      Host architecture of toolchains to use (default: ${hostarch})
55 -b [DIR]       Build location (default: ${build_location})
56 -m [MODE]      Test mode user/system (default: ${test_mode})
57 -e             Disable -Werror
58 -l             Build with LLVM/clang
59EOF
60}
61
62while true; do
63	case "$1" in
64		'-j')
65			nproc="$2"
66			shift 2; continue ;;
67		'-p')
68			perform_download=1
69			shift; continue ;;
70		'-d')
71			download_location="$2"
72			shift 2; continue ;;
73		'-c')
74			crosstool_version="$2"
75			shift 2; continue ;;
76		'-a')
77			hostarch="$2"
78			shift 2; continue ;;
79		'-b')
80			build_location="$(realpath "$2")"
81			shift 2; continue ;;
82		'-m')
83			test_mode="$2"
84			shift 2; continue ;;
85		'-e')
86			werror=0
87			shift; continue ;;
88		'-l')
89			llvm=1
90			shift; continue ;;
91		'-h')
92			print_usage
93			exit 0
94			;;
95		'--')
96			shift; break ;;
97		*)
98			echo 'Internal error!' >&2; exit 1 ;;
99	esac
100done
101
102if [[ -n "$*" ]]; then
103	archs="$*"
104fi
105
106crosstool_arch() {
107	case "$1" in
108	arm64) echo aarch64;;
109	armthumb) echo arm;;
110	ppc) echo powerpc;;
111	ppc64) echo powerpc64;;
112	ppc64le) echo powerpc64;;
113	riscv) echo riscv64;;
114	loongarch) echo loongarch64;;
115	mips*) echo mips;;
116	s390*) echo s390;;
117	sparc*) echo sparc64;;
118	x32*) echo x86_64;;
119	*) echo "$1";;
120	esac
121}
122
123crosstool_abi() {
124	case "$1" in
125	arm | armthumb) echo linux-gnueabi;;
126	*) echo linux;;
127	esac
128}
129
130download_crosstool() {
131	arch="$(crosstool_arch "$1")"
132	abi="$(crosstool_abi "$1")"
133
134	archive_name="${hostarch}-gcc-${crosstool_version}-nolibc-${arch}-${abi}.tar.gz"
135	url="https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/${hostarch}/${crosstool_version}/${archive_name}"
136	archive="${download_location}${archive_name}"
137	stamp="${archive}.stamp"
138
139	[ -f "${stamp}" ] && return
140
141	echo "Downloading crosstools ${arch} ${crosstool_version}"
142	mkdir -p "${download_location}"
143	curl -o "${archive}" --fail --continue-at - "${url}"
144	tar -C "${download_location}" -xf "${archive}"
145	touch "${stamp}"
146}
147
148# capture command output, print it on failure
149# mimics chronic(1) from moreutils
150function swallow_output() {
151	if ! OUTPUT="$("$@" 2>&1)"; then
152		echo "$OUTPUT"
153		return 1
154	fi
155	return 0
156}
157
158test_arch() {
159	arch=$1
160	ct_arch=$(crosstool_arch "$arch")
161	ct_abi=$(crosstool_abi "$1")
162
163	if [ ! -d "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/." ]; then
164		echo "No toolchain found in ${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}."
165		echo "Did you install the toolchains or set the correct arch ? Rerun with -h for help."
166		return 1
167	fi
168
169	cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-")
170	build_dir="${build_location}/${arch}"
171	if [ "$werror" -ne 0 ]; then
172		CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror"
173	fi
174	MAKE=(make -f Makefile.nolibc -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}")
175
176	case "$test_mode" in
177		'system')
178			test_target=run
179			;;
180		'user')
181			test_target=run-user
182			;;
183		*)
184			echo "Unknown mode $test_mode"
185			exit 1
186	esac
187	printf '%-15s' "$arch:"
188	if [ "$arch" = "s390" ] && ([ "$llvm" = "1" ] || [ "$test_mode" = "user" ]); then
189		echo "Unsupported configuration"
190		return
191	fi
192	if [ "$arch" = "m68k" -o "$arch" = "sh4" ] && [ "$llvm" = "1" ]; then
193		echo "Unsupported configuration"
194		return
195	fi
196	if [ "$arch" = "x32" ] && [ "$test_mode" = "user" ]; then
197		echo "Unsupported configuration"
198		return
199	fi
200
201	mkdir -p "$build_dir"
202	swallow_output "${MAKE[@]}" defconfig
203	swallow_output "${MAKE[@]}" CFLAGS_EXTRA="$CFLAGS_EXTRA" "$test_target" V=1
204	cp run.out run.out."${arch}"
205	"${MAKE[@]}" report | grep passed
206}
207
208if [ "$perform_download" -ne 0 ]; then
209	for arch in $archs; do
210		download_crosstool "$arch"
211	done
212fi
213
214for arch in $archs; do
215	test_arch "$arch"
216done
217