#!/usr/bin/env bash

# src/tools/tidy

# This script checks C++ code with clang-tidy in the following location:
#
# src/backend/gporca
#
# To genarate a CMake build directory with compilation database:
#
# for debug build:
#
# $ CXX=clang++ cmake -GNinja -Hsrc/backend/gporca -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -Bbuild.debug
#
# and release:
#
# $ CXX=clang++ cmake -GNinja -Hsrc/backend/gporca -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -Bbuild.release
#
# To check (it's unsurprising if we pass the check in one configuration but not another)
# $ src/tools/tidy chk-orca build.debug
# and
# $ src/tools/tidy chk-orca build.release
#
# If clang-tidy is under a different name, or it's not in your PATH, specify it
# in the CLANG_TIDY environment variable.
#
# To install clang-tidy:
#   macOS: brew install llvm (typically clang-tidy will be under /usr/local/opt/llvm/bin)
#   Debian derivatives: apt install clang-tidy
#   Or consult your operating system's package manager
#
# For more details about running clang-tidy against ORCA, see src/backend/gporca/README.tidy.md

set -e -u -o pipefail

: "${CLANG_TIDY:=clang-tidy}"

# This depends on GNU parallel (https://www.gnu.org/software/parallel/). To install:
#   macOS: brew install parallel
#   Debian-derivatives: apt install parallel
chk() {
	local build_dir compdb
	build_dir=$1
	compdb=$1/compile_commands.json
	test -f "${compdb}" || die "compilation database not found at ${compdb}"

	parallel -0 -q --halt now,fail=1 \
		"${CLANG_TIDY}" -p "${build_dir}" -quiet
}

chk_orca() {
	local build_dir
	build_dir=$1
	orca_files_to_tidy | chk "${build_dir}"
}

chk_gpopt() {
	local vpath
	vpath=$1
	gpopt_files_to_tidy | chk "${vpath}"
}

usage() {
	printf >&2 "Usage: [CLANG_TIDY=clang-tidy] %s (chk-gpopt|chk-orca) /path/to/build-dir\n" "$0"
	echo >&2 "Make sure a compilation database (compile_commands.json) exists in the build directory you specified"
	echo >&2 "compilation databases are typically generated by CMake (using -DCMAKE_EXPORT_COMPILE_COMMANDS=ON) or interceptors like bear"

	return 1
}

die() {
	local msg
	msg=$1

	echo >&2 "${msg}"

	exit 1
}

# NUL-delimited list of files to tidy
orca_files_to_tidy() {
	git ls-files -z 'src/backend/gporca/*.cpp'
}

gpopt_files_to_tidy() {
	git ls-files -z 'src/backend/gpopt/*.cpp'
}

_main() {
	local cmd
	cmd=${1:-}
	shift
	local TOPLEVEL
	TOPLEVEL=$(git rev-parse --show-toplevel)
	cd "${TOPLEVEL}"
	case "${cmd}" in
	chk-gpopt)
		chk_gpopt "$@"
		;;
	chk-orca)
		chk_orca "$@"
		;;
	*)
		usage
		;;
	esac
}

_main "$@"
