function install_ittapi() {
	# Install Intel Instrumentation and Trace API
	local ittapi_version=v3.24.5 ittapi_dir=/usr/src/ittapi

	rm -rf "$ittapi_dir"
	git clone "${GIT_REPO_ITTAPI}" --branch "$ittapi_version" "$GIT_REPOS/ittapi"
	make -C "$GIT_REPOS/ittapi/src/ittnotify_refcol"

	mv "$GIT_REPOS/ittapi" "$ittapi_dir"
	ln -s . "$ittapi_dir/sdk"
}

function install_fio() {
	# This version of fio is installed in /usr/src/fio to enable
	# building the spdk fio plugin.
	local fio_version="fio-3.35"
	local fio_dir=/usr/src/fio

	rm -rf "$fio_dir"
	git clone "${GIT_REPO_FIO}" --branch "${fio_version}" "$GIT_REPOS/fio"
	mv "$GIT_REPOS/fio" "${fio_dir}"

	if [ $OSID == 'freebsd' ]; then
		gmake -C "${fio_dir}" -j${jobs} \
			&& sudo gmake -C "${fio_dir}" install
	else
		make -C "${fio_dir}" -j${jobs} \
			&& sudo make -C "${fio_dir}" install
	fi
}

function install_flamegraph() {
	# Flamegraph is used when printing out timing graphs for the tests.
	local flamegraph_dir=/usr/local/FlameGraph

	rm -rf "$flamegraph_dir"
	git clone "${GIT_REPO_FLAMEGRAPH}" "$GIT_REPOS/FlameGraph"
	mkdir -p /usr/local
	mv "$GIT_REPOS/FlameGraph" "$flamegraph_dir"
}

function _install_qemu() {
	local repo=$1
	local version=$2
	local prefix=${3:-}
	local name=${4:-}

	mkdir -p "$GIT_REPOS/qemu"

	local repo_dir=$GIT_REPOS/qemu/$version
	if [[ -n $prefix ]]; then
		repo_dir=$GIT_REPOS/qemu/$prefix-$version
	fi

	if [[ ! -d $repo_dir ]]; then
		git clone "$repo" -b "$version" "$repo_dir"
	else
		echo "qemu already checked out. Skipping"
	fi

	declare -a opt_params=("--prefix=/usr/local/qemu/${repo_dir##*/}")
	declare -a extra_cflags=()

	opt_params+=("--disable-docs")
	if [[ $CC == *gcc* ]] && ((cc_version >= 9)); then
		opt_params+=("--disable-glusterfs")
	fi

	extra_cflags+=("-Wno-error")

	# Most tsocks proxies rely on a configuration file in /etc/tsocks.conf.
	# If using tsocks, please make sure to complete this config before trying to build qemu.
	if [[ $INSTALL_TSOCKS == true && $NO_TSOCKS != true ]]; then
		if hash tsocks 2> /dev/null; then
			opt_params+=("--with-git='tsocks git'")
		fi
	fi
	opt_params+=("--extra-cflags=${extra_cflags[*]}")

	if [[ $prefix == vhost ]]; then
		# Latest qemu seems to take sysconfdir from the prefix and instead of checking /etc
		# it looks under /usr/local/qemu/vhost*/bin/../etc which is a bit peculiar. Fix it.
		opt_params+=("--sysconfdir=/etc/")
	fi

	# The qemu configure script places several output files in the CWD.
	(cd "$repo_dir" && ./configure "${opt_params[@]}" --target-list="x86_64-softmmu" --enable-kvm --enable-linux-aio --enable-numa)

	make -C "$repo_dir" -j${jobs}
	sudo make -C "$repo_dir" install

	# Add a symlink to point at a latest build - this is useful to easily track QEMU flavors for which
	# versions change quite often (e.g. vfio-user's).
	[[ -n $name ]] || return 0
	[[ -L /usr/local/qemu/$name-latest ]] && sudo rm "/usr/local/qemu/$name-latest"
	sudo ln -s "/usr/local/qemu/${repo_dir##*/}" "/usr/local/qemu/$name-latest"
}

function install_qemu() {
	# Two versions of QEMU are installed directly from the source.
	# Each QEMU is dedicated for different use-case:
	#  - vfio-user QEMU: version used to test libvfio-user components.
	#  - vhost QEMU: Used to boot VMs from within vhost tests.

	_install_qemu $GIT_REPO_QEMU $VFIO_QEMU_VERSION "" vfio-user
	_install_qemu "$GIT_REPO_QEMU" "$VHOST_QEMU_VERSION" vhost vhost
}

function install_nvmecli() {
	# nvme-cli >1.11.1 should be used.
	local nvmecli_version="v2.16"
	local nvmecli_dir=/usr/local/src/nvme-cli

	rm -rf "$GIT_REPOS/nvme-cli-cuse"
	git clone "${GIT_REPO_NVME_CLI_GITHUB}" --branch "${nvmecli_version}" "$GIT_REPOS/nvme-cli-cuse"

	meson setup --force-fallback-for=libnvme \
		"$GIT_REPOS/nvme-cli-cuse/.build" \
		"$GIT_REPOS/nvme-cli-cuse"
	meson compile -C "$GIT_REPOS/nvme-cli-cuse/.build"

	rm -rf "${nvmecli_dir}"
	mv "$GIT_REPOS/nvme-cli-cuse" "${nvmecli_dir}"

	# Make sure binary is available for the cuse tests
	if [[ -e "${nvmecli_dir}"/.build/nvme ]]; then
		sudo ln -s .build/nvme "${nvmecli_dir}"
	fi
}

# This function install version of nvme-cli, that support listing spdk nvme
# devices, should be remove after changes present in nvme-cli upstream.
function install_nvmecli_plugin() {
	rm -rf "$GIT_REPOS/nvme-cli-plugin"

	git clone $GIT_REPO_NVME_CLI "$GIT_REPOS/nvme-cli-plugin"
	git -C "$GIT_REPOS/nvme-cli-plugin" fetch $GIT_REPO_NVME_CLI refs/changes/95/16795/12
	git -C "$GIT_REPOS/nvme-cli-plugin" checkout FETCH_HEAD

	meson setup --force-fallback-for=libnvme,json-c \
		"$GIT_REPOS/nvme-cli-plugin/.build" \
		"$GIT_REPOS/nvme-cli-plugin"
	meson compile -C "$GIT_REPOS/nvme-cli-plugin/.build"

	rm -rf /usr/local/src/nvme-cli-plugin
	mv "$GIT_REPOS/nvme-cli-plugin" /usr/local/src/nvme-cli-plugin

	# Make sure binary is available for the plugin tests
	if [[ -e /usr/local/src/nvme-cli-plugin/.build/nvme ]]; then
		sudo ln -s .build/nvme /usr/local/src/nvme-cli-plugin/
	fi
}

function install_libiscsi() {
	# We currently don't make any changes to the libiscsi repository for our tests, but it is possible that we will need
	# to later. Cloning from git is just future proofing the machines.
	rm -rf "$GIT_REPOS/libiscsi"
	git clone "${GIT_REPO_LIBISCSI}" "$GIT_REPOS/libiscsi"
	(cd "$GIT_REPOS/libiscsi" && ./autogen.sh && ./configure --prefix=/usr/local/libiscsi)
	make -C "$GIT_REPOS/libiscsi" -j${jobs} WARN_CFLAGS=
	sudo make -C "$GIT_REPOS/libiscsi" install
}

function install_libblkio() {
	rm -rf "$GIT_REPOS/libblkio"
	git clone "${GIT_REPO_LIBBLKIO}" "$GIT_REPOS/libblkio"
	meson setup "$GIT_REPOS/libblkio/.build" "$GIT_REPOS/libblkio"
	meson compile -C "$GIT_REPOS/libblkio/.build"
	meson install -C "$GIT_REPOS/libblkio/.build"
	echo "/usr/local/lib64" > /etc/ld.so.conf.d/spdk-libblkio.conf
	ldconfig -X
}

function install_git() {
	if type -P git; then
		if ge "$(git --version | awk '{print $3}')" "$GIT_VERSION"; then
			return 0
		fi
	fi >/dev/null

	install zlib-devel curl-devel
	tar -C "$GIT_REPOS" -xzof <(wget -qO- "$GIT_REPO_GIT")
	(cd "$GIT_REPOS/git-$GIT_VERSION" \
		&& make configure \
		&& ./configure \
		&& sudo make -j${jobs} install)
}

function install_extra_pkgs() {
	if [[ $INSTALL_QAT == true ]]; then
		install libudev-devel || install libudev-dev || :
	fi

	if [[ $INSTALL_QEMU == true ]]; then
		install qemu-system-x86 qemu-img \
			|| install qemu-system-x86 qemu-utils \
			|| install qemu

		# Install extra dependency which was removed from Qemu 7.2 source tree
		install libslirp-devel \
			|| install libslirp-dev
	fi || :
}

function install_vagrant() {
	local vagrant_version="2.2.7"
	local vagrant_installer="vagrant_${vagrant_version}_x86_64.deb"
	local vagrant_plugins=(vagrant-libvirt vagrant-sshfs vagrant-cachier vagrant-proxyconf)

	if [[ $OSID != ubuntu ]]; then
		echo "Currently, Vagrant installation is supported only on ubuntu"
		return 0
	fi

	# Install vagrant and it's plugins dependencies
	# function should be defined in pkgdep/$package_manager file
	install_vagrant_dependencies

	# Download and install vagrant
	if hash vagrant &> /dev/null; then
		echo "Vagrant is already installed"
	else
		wget "https://releases.hashicorp.com/vagrant/${vagrant_version}/${vagrant_installer}"
		sudo dpkg -i "${vagrant_installer}"
	fi
	vagrant --version

	# Install vagrant plugins
	local vagrant_plugin_list
	vagrant_plugin_list=$(vagrant plugin list)

	local plugin
	for plugin in "${vagrant_plugins[@]}"; do
		if grep -Fq "$plugin" <<< "$vagrant_plugin_list"; then
			echo "$plugin already installed"
		else
			vagrant plugin install "$plugin"
		fi
	done
}

function install_igb_uio() {
	rm -rf "$GIT_REPOS/dpdk-kmods"
	git clone "${GIT_REPO_DPDK_KMODS}" "$GIT_REPOS/dpdk-kmods"

	(cd "$GIT_REPOS/dpdk-kmods/linux/igb_uio" && make -j ${jobs})
	sudo mkdir -p "/lib/modules/$(uname -r)/extra/dpdk"
	sudo cp "$GIT_REPOS/dpdk-kmods/linux/igb_uio/igb_uio.ko" "/lib/modules/$(uname -r)/extra/dpdk"
	sudo depmod
}

function install_contigmem() {
	# Dedicated solely for freebsd
	[[ $OSID == freebsd ]] || return 0

	rm -rf "$GIT_REPOS/dpdk-stable"

	git clone \
		--branch "$DPDK_STABLE_BRANCH" \
		--depth 1 "$DPDK_STABLE_REPO" \
		"$GIT_REPOS/dpdk-stable"

	# We skip most of the stuff to not waste time during setup but at the very end it won't
	# matter much as we want to build a single, specific target anyway. Note that DPDK considers
	# enable_kmods to be deprecated and as such, for freebsd kernel modules are always enabled,
	# so we don't need to set it in.
	meson setup \
		-Ddisable_apps="*" \
		-Ddisable_libs="*" \
		-Ddisable_drivers="*/*" \
		-Dtests=false \
		"$GIT_REPOS/dpdk-stable/build" \
		"$GIT_REPOS/dpdk-stable"

	# The thing we are after
	ninja -C "$GIT_REPOS/dpdk-stable/build" \
		kernel/freebsd/contigmem.ko

	[[ -e $GIT_REPOS/dpdk-stable/build/kernel/freebsd/contigmem.ko ]] || return 1

	# Put it into one of the default search paths
	cp -p \
		"$GIT_REPOS/dpdk-stable/build/kernel/freebsd/contigmem.ko" \
		"/boot/modules"

	# Make sure kernel loads it early on. Don't put it into the main loader file though, define
	# custom one. Also, provide a default setup of 2 * 1GB buffers - this should match default
	# hugepages requirement that autotest enforces under freebsd.
	cat <<-LOADER >/boot/loader.conf.d/spdk.conf
		contigmem_load="YES"
		hw.contigmem.num_buffers="2"
		hw.contigmem.buffer_size="$((1 << 30))"
	LOADER
}

function install_libbpf() {
	local libbpf_version=v1.4.5

	rm -rf "$GIT_REPOS/libbpf"
	git clone "$GIT_REPO_LIBBPF" --branch "$libbpf_version" "$GIT_REPOS/libbpf"

	make -C "$GIT_REPOS/libbpf/src" -j install
	# install target doesn't include the kernel header files
	make -C "$GIT_REPOS/libbpf/src" install_uapi_headers
}

function install_bpftrace() {
	local deps_fedora=() deps_ubuntu=() bcc_rev

	deps_fedora+=(cereal-devel)
	deps_fedora+=(clang-devel)
	deps_fedora+=(dwarves)
	deps_fedora+=(gmock-devel)
	deps_fedora+=(gtest-devel)
	deps_fedora+=(llvm-devel)
	deps_fedora+=(bcc-devel)
	deps_fedora+=(libbpf-devel)

	deps_ubuntu+=(libcereal-dev)
	deps_ubuntu+=(libclang-dev)
	deps_ubuntu+=(llvm-dev)
	# Under jammy (2204) the libbpf version is not compatible with the version of
	# bpftrace that we are using. Instead, we are going to provide our own build
	# of libbpf, including both up-to-date bpf.h and linux/bpf.h.
	[[ $VERSION_CODENAME == jammy ]] || deps_ubuntu+=(libbpf-dev)
	deps_ubuntu+=(libbpfcc-dev)
	deps_ubuntu+=(libelf-dev)
	deps_ubuntu+=(binutils-dev)

	local -n deps="deps_$ID"

	((${#deps[@]} > 0)) || return 1

	deps+=(clang cmake)

	install "${deps[@]}"

	if [[ $VERSION_CODENAME == jammy ]]; then
		install_libbpf
	fi

	rm -rf $GIT_REPOS/bpftrace

	git clone "$GIT_REPO_BPFTRACE" "$GIT_REPOS/bpftrace"
	git -C $GIT_REPOS/bpftrace checkout $BPFTRACE_VERSION

	mkdir -p "$GIT_REPOS/bpftrace/build"
	cmake \
		-DCMAKE_BUILD_TYPE=Release \
		-DBUILD_TESTING=OFF \
		-B "$GIT_REPOS/bpftrace/build" \
		-S "$GIT_REPOS/bpftrace"

	make -C $GIT_REPOS/bpftrace/build -j$(nproc)
	sudo make -C $GIT_REPOS/bpftrace/build install
}

function install_doxygen() {
	# Stable, 1.10 commit that works for our docs
	local release=78422d3905e57acebf0374feefafa6578dbe86aa

	rm -rf "$GIT_REPOS/doxygen"

	git clone "$GIT_REPO_DOXYGEN" "$GIT_REPOS/doxygen"
	git -C "$GIT_REPOS/doxygen" checkout "$release"

	mkdir -p "$GIT_REPOS/doxygen/build"

	cmake -G "Unix Makefiles" \
		-B "$GIT_REPOS/doxygen/build" \
		-S "$GIT_REPOS/doxygen"

	# This build is quite heavy, so let's not go crazy with -j here
	make -C "$GIT_REPOS/doxygen/build" -j$(($(nproc) / 2))
	make -C "$GIT_REPOS/doxygen/build" install
}

function install_sources() {
	if [[ $ID == centos ]] && (( VERSION_ID == 7 )); then
		# install proper version of the git first
		install_git
	fi

	IFS="," read -ra conf_env <<< "$CONF"
	for conf in "${conf_env[@]}"; do
		export "INSTALL_${conf^^}=true"
	done

	if [[ $OSID == freebsd ]]; then
		jobs=$(($(sysctl -n hw.ncpu) * 2))
		sources+=(install_contigmem)
	else
		jobs=$(($(nproc) * 2))
		sources+=(
			install_libiscsi
			install_libblkio
			install_nvmecli
			install_nvmecli_plugin
			install_qemu
			install_igb_uio
			install_bpftrace
			install_doxygen
		)
		install_extra_pkgs
	fi
	sources+=(install_fio)
	sources+=(install_flamegraph)
	sources+=(install_vagrant)
	sources+=(install_ittapi)

	sudo mkdir -p /usr/{,local}/src
	sudo mkdir -p "$GIT_REPOS"

	for source in "${sources[@]}"; do
		source_conf=${source^^}
		if [[ ${!source_conf} == true ]]; then
			"$source"
		fi
	done
}

GIT_VERSION=2.25.1
BPFTRACE_VERSION=${BPFTRACE_VERSION:-v0.24.1}
VFIO_QEMU_VERSION=${VFIO_QEMU_VERSION:-v10.1.1}
VHOST_QEMU_VERSION=${VHOST_QEMU_VERSION:-v8.0.0}
DPDK_STABLE_BRANCH=${DPDK_STABLE_BRANCH:-24.11}

: ${GIT_REPO_FIO=https://github.com/axboe/fio.git}
export GIT_REPO_FIO
: ${GIT_REPO_FLAMEGRAPH=https://github.com/brendangregg/FlameGraph.git}
export GIT_REPO_FLAMEGRAPH
: ${GIT_REPO_QEMU=https://github.com/qemu/qemu}
export GIT_REPO_QEMU
: ${GIT_REPO_LIBISCSI=https://github.com/sahlberg/libiscsi}
export GIT_REPO_LIBISCSI
: ${GIT_REPO_LIBBLKIO=https://gitlab.com/libblkio/libblkio}
export GIT_REPO_LIBBLKIO
: ${DRIVER_LOCATION_QAT=https://downloadmirror.intel.com/828487/QAT.L.4.26.0-00008.tar.gz}
export DRIVER_LOCATION_QAT
: ${GIT_REPO_GIT=https://github.com/git/git/archive/v${GIT_VERSION}.tar.gz}
export GIT_REPO_GIT
: ${GIT_REPO_DPDK_KMODS=http://dpdk.org/git/dpdk-kmods}
export GIT_REPO_DPDK_KMODS
: ${GIT_REPO_BCC=https://github.com/iovisor/bcc.git}
export GIT_REPO_BCC
: ${GIT_REPO_BPFTRACE=https://github.com/iovisor/bpftrace.git}
export GIT_REPO_BPFTRACE
: ${GIT_REPO_NVME_CLI=https://review.spdk.io/gerrit/spdk/nvme-cli}
export GIT_REPO_NVME_CLI
: ${GIT_REPO_NVME_CLI_GITHUB=https://github.com/linux-nvme/nvme-cli.git}
export GIT_REPO_NVME_CLI_GITHUB
: ${GIT_REPO_ITTAPI=https://github.com/intel/ittapi.git}
export GIT_REPO_ITTAPI
: ${GIT_REPO_DOXYGEN="https://github.com/doxygen/doxygen"}
export GIT_REPO_DOXYGEN
: ${GIT_REPO_LIBBPF="https://github.com/libbpf/libbpf"}
export GIT_REPO_LIBBPF
: ${DPDK_STABLE_REPO="http://dpdk.org/git/dpdk-stable"}

GIT_REPOS=${GIT_REPOS:-$HOME}
CC=${CC:-gcc}

if ! cc_version=$($CC -dumpversion 2> /dev/null); then
	echo "'$CC' version could not be determined, aborting" >&2
	return 1
fi

cc_version=${cc_version%%.*}
if [[ -e /proc/sys/kernel/osrelease ]]; then
	kernel_ver=$(< /proc/sys/kernel/osrelease)
fi
