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