#!/bin/sh

set -e

ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)" || exit 1
TARGET="${TARGET:-mips-linux-gnu}"
CROSS="${CROSS:-${TARGET}-}"
export CROSS_CFLAGS="-O0"
# We will slowly extend the supported tests as soon as we fix them
# R2R_TESTS="${R2R_TESTS:-test/db/cmd}"
R2R_TESTS="${R2R_TESTS:-test/db/cmd/echo test/db/cmd/archs test/db/cmd/cmd_print_misc test/db/cmd/cmd_print test/db/cmd/cmd_print_bitformat test/db/cmd/cmd_ps test/db/cmd/cmd_cmp test/db/cmd/cmd_pd test/db/cmd/cmd_pd2 test/db/cmd/cmd_pde test/db/cmd/cmd_pd_str test/db/cmd/cmd_pf2 test/db/cmd/cmd_ao test/db/cmd/cmd_ab test/db/cmd/cmd_aae test/db/cmd/cmd_af test/db/cmd/cmd_afl test/db/cmd/cmd_afb test/db/cmd/cmd_afn test/db/cmd/cmd_afv test/db/cmd/cmd_anal_dyncc test/db/cmd/cmd_anal_dex_dyncc test/db/cmd/cmd_ax test/db/cmd/cmd_ag test/db/cmd/cmd_graph test/db/cmd/cmd_pae test/db/cmd/m68k test/db/cmd/cmd_w test/db/cmd/cmd_hash test/db/cmd/cmd_question test/db/cmd/cmd_alias test/db/cmd/cmd_open test/db/cmd/cmd_yank test/db/cmd/cmd_help test/db/cmd/cmd_idp test/db/cmd/newprj test/db/cmd/cmd_b test/db/cmd/cmd_pseudo_arm64 test/db/cmd/cmd_search_crypto test/db/asm/mips_v2_64 test/db/json test/db/formats/elf test/db/formats/mdmp test/db/formats/coff test/db/formats/web_assembly test/db/formats/qnx test/db/formats/xtac test/db/formats/dotnet test/db/formats/xbe test/db/formats/nso test/db/formats/pebble test/db/formats/le test/db/formats/vsf test/db/formats/pe test/db/formats/dmp test/db/formats/bflt test/db/formats/mach0/fatmach0 test/db/formats/mach0/objc test/db/formats/mach0/coresymbolication test/db/formats/mach0/create test/db/formats/mach0/entitlements test/db/formats/mach0/fatio test/db/formats/mach0/fcn test/db/formats/mach0/headers test/db/formats/mach0/ioswizbug test/db/formats/mach0/leb128 test/db/formats/mach0/mach0 test/db/formats/mach0/redacted test/db/formats/mach0/sdb test/db/formats/mach0/strip test/db/formats/mach0/swift test/db/formats/mach0/thumb test/db/formats/ar test/db/formats/cgc test/db/formats/dex test/db/formats/dol test/db/formats/dwarf test/db/formats/firmware test/db/formats/gba test/db/formats/gbc test/db/formats/hunk test/db/formats/java test/db/formats/jni test/db/formats/mangling/bin test/db/formats/mangling/cxx test/db/formats/mangling/mangling test/db/formats/mangling/msvc test/db/formats/mangling/rust test/db/formats/mangling/swift test/db/formats/mdt test/db/formats/menuet test/db/formats/msil test/db/formats/msx test/db/formats/mz test/db/formats/ne test/db/formats/nes test/db/formats/ninds test/db/formats/nro test/db/formats/omf test/db/formats/pdb test/db/formats/pef test/db/formats/plan9 test/db/formats/prg test/db/formats/psx test/db/formats/pyc test/db/formats/rel test/db/formats/sbpf test/db/formats/sep test/db/formats/smd test/db/formats/sms test/db/formats/som test/db/formats/spc700 test/db/formats/symbols test/db/formats/xcoff test/db/formats/xcoff64 test/db/formats/z64 test/db/formats/zimg test/db/formats/zip}"
R2R_TIMEOUT="${R2R_TIMEOUT:-120}"
UNIT_TESTS="${UNIT_TESTS:-test_base64 test_bitmap test_bitset test_hex test_json test_list test_math test_str test_uleb128}"
MODE="${1:-all}"

usage() {
	echo "Usage: sys/mipsbe.sh [build|smoke|unit|all]"
	exit 1
}

case "$MODE" in
build|smoke|unit|all)
	;;
-h|--help)
	usage
	;;
*)
	usage
	;;
esac

find_tool() {
	if command -v "$1" >/dev/null 2>&1; then
		command -v "$1"
		return 0
	fi
	if [ -x "$1" ]; then
		echo "$1"
		return 0
	fi
	return 1
}

need_file() {
	if [ ! -e "$1" ]; then
		echo "Missing expected file: $1"
		exit 1
	fi
}

unit_bins() {
	for unit in $UNIT_TESTS ; do
		printf ' bin/%s' "$unit"
	done
}

build_unit_tests() {
	unit_targets="$(unit_bins)"
	if [ -z "$unit_targets" ]; then
		return 0
	fi
	libatomic="$(sed -n 's/^LIBATOMIC=//p' config-user.mk | tail -n 1)"
	unit_ldlibs="../../libr/libr.a -lm -ldl -pthread -lutil ${libatomic}"
	# shellcheck disable=SC2086
	make -B -C test/unit $unit_targets \
		CC="$CC" \
		LIBDIR="${ROOT}/libr" \
		INCLUDEDIR="${ROOT}/libr/include" \
		LDLIBS="$unit_ldlibs"
}

make_qemu_wrapper() {
	out="$1"
	target="$2"
	cat > "$out" <<EOF
#!/bin/sh
QEMU_LD_PREFIX="${QEMU_LD_PREFIX_DIR}"
export QEMU_LD_PREFIX
exec "$QEMU_RUN" "$target" "\$@"
EOF
	chmod +x "$out"
}

run_qemu() {
	if [ -n "$QEMU_LD_PREFIX_DIR" ]; then
		QEMU_LD_PREFIX="$QEMU_LD_PREFIX_DIR" "$QEMU_RUN" "$@"
	else
		"$QEMU_RUN" "$@"
	fi
}

run_target() {
	if [ -n "$QEMU_RUN" ]; then
		run_qemu "$@"
	else
		"$@"
	fi
}

run_r2r() {
	if [ -n "$QEMU_R2R" ]; then
		run_qemu "$R2R_BIN" "$@"
	else
		"$R2R_BIN" "$@"
	fi
}

cd "$ROOT" || exit 1

if [ "$MODE" = build ] || [ "$MODE" = all ]; then
	CC="${CC:-$(find_tool "${CROSS}gcc" || true)}"
	if [ -z "$CC" ]; then
		echo "Missing required tool: ${CROSS}gcc"
		exit 1
	fi
	export CC CROSS
	export BUILD_R2R="${BUILD_R2R:-1}"
	export BUILD_BINR="${BUILD_BINR:-1}"
	export CROSS_WITH_GPL=1
	export CONFIGURE_PLUGINS_ARGS="${CONFIGURE_PLUGINS_ARGS:---without-zydis}"
	export CFGARGS="${CFGARGS:---without-zydis}"
	export PLUGINS_CFG="dist/plugins-cfg/plugins.def.cfg"
	sys/cross.sh "$TARGET"
	build_unit_tests
fi

R2R_BIN="${ROOT}/binr/r2r/r2r"
RADARE2_BIN="${ROOT}/binr/blob/radare2"
RASM2_BIN="${ROOT}/binr/blob/rasm2"
need_file "$R2R_BIN"
need_file "$RADARE2_BIN"
need_file "$RASM2_BIN"

READELF="${READELF:-$(find_tool "${CROSS}readelf" || true)}"
if [ -n "$READELF" ]; then
	"$READELF" -h "${ROOT}/binr/blob/r2blob" | grep -q "Data:.*big endian" || {
		echo "binr/blob/r2blob is not a big-endian ELF"
		exit 1
	}
	"$READELF" -h "${ROOT}/binr/blob/r2blob" | grep -q "Machine:.*MIPS" || {
		echo "binr/blob/r2blob is not a MIPS ELF"
		exit 1
	}
fi
if [ "$MODE" = build ]; then
	exit 0
fi
if [ "$MODE" != smoke ]; then
	for unit in $UNIT_TESTS ; do
		need_file "${ROOT}/test/unit/bin/$unit"
	done
fi

QEMU_R2R=
QEMU_RUN=
QEMU_LD_PREFIX_DIR=
WRAP_DIR=
if "$R2R_BIN" -v >/dev/null 2>&1 && "$RADARE2_BIN" -v >/dev/null 2>&1; then
	R2_BIN_PATH="${ROOT}/binr/blob"
	R2R_RADARE2_BIN="$RADARE2_BIN"
	R2R_RASM2_BIN="$RASM2_BIN"
	PATH="${ROOT}/binr/blob:${PATH}"
else
	QEMU_RUN="${QEMU:-$(find_tool qemu-mips || find_tool qemu-mips-static || true)}"
	if [ -z "$QEMU_RUN" ]; then
		echo "Cannot run MIPS binaries. Install qemu-user or qemu-user-static."
		exit 1
	fi
	QEMU_LD_PREFIX_DIR="${QEMU_LD_PREFIX:-}"
	if [ -z "$QEMU_LD_PREFIX_DIR" ] && [ -d "/usr/${TARGET}" ]; then
		QEMU_LD_PREFIX_DIR="/usr/${TARGET}"
	fi
	run_qemu "$R2R_BIN" -v >/dev/null || {
		echo "Cannot run $R2R_BIN with $QEMU_RUN"
		exit 1
	}
	QEMU_R2R="$QEMU_RUN"
	WRAP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/r2-mipsbe.XXXXXX")" || exit 1
	trap 'rm -rf "$WRAP_DIR"' EXIT HUP INT TERM
	mkdir -p "$WRAP_DIR/home" "$WRAP_DIR/data" "$WRAP_DIR/config"
	HOME="$WRAP_DIR/home"
	XDG_DATA_HOME="$WRAP_DIR/data"
	XDG_CONFIG_HOME="$WRAP_DIR/config"
	for bin in r2 radare2 rabin2 rarun2 rasm2 ragg2 rahash2 rax2 ravc2 rafind2 radiff2 rafs2 ; do
		if [ -e "${ROOT}/binr/blob/$bin" ]; then
			make_qemu_wrapper "$WRAP_DIR/$bin" "${ROOT}/binr/blob/$bin"
		fi
	done
	R2_BIN_PATH="$WRAP_DIR"
	R2R_RADARE2_BIN="$WRAP_DIR/radare2"
	R2R_RASM2_BIN="$WRAP_DIR/rasm2"
	PATH="${WRAP_DIR}:${PATH}"
	R2R_SKIP_LEAK="${R2R_SKIP_LEAK:-1}"
fi

export PATH
export HOME
export XDG_DATA_HOME
export XDG_CONFIG_HOME
export R2R_SKIP_LEAK
export R2_BIN="$R2_BIN_PATH"
export R2R_RADARE2="$R2R_RADARE2_BIN"
export R2R_RASM2="$R2R_RASM2_BIN"
export R2R_JOBS="${R2R_JOBS:-1}"
export R2_MAGICPATH="${R2_MAGICPATH:-${ROOT}/libr/magic/d/default}"
need_file "$R2_MAGICPATH"

R2R_OUTPUT_ARG=
if [ -n "$R2R_OUTPUT" ]; then
	R2R_OUTPUT_ARG="-o $R2R_OUTPUT"
fi

if [ "$MODE" != unit ]; then
	# shellcheck disable=SC2086
	run_r2r -t "$R2R_TIMEOUT" $R2R_OUTPUT_ARG $R2R_TESTS
fi

if [ "$MODE" != smoke ]; then
	status=0
	(
		cd test || exit 1
		export R2_DEBUG_ASSERT=1
		for unit in $UNIT_TESTS ; do
			echo "unit/bin/$unit"
			run_target "${ROOT}/test/unit/bin/$unit" || status=1
		done
		exit $status
	)
fi
