# The TiKV Makefile
#
# These are mostly light rules around cargo and in general developers
# can do daily development using cargo alone. It does do a few key
# things that developers need to understand:
#
# It turns on features that are not on by default in the cargo
# manifest but that are part of the release build (see the
# ENABLE_FEATURES variable).
#
# The `clippy` rule runs clippy with custom configuration, so
# is the correct way to run clippy against TiKV.
#
# The `test` rule runs tests in configurations that are not covered by `cargo
# test` by default.
#
# Important make rules:
#
# - `build` - create a development profile, unoptimized build
#
# - `run` - run a development build
#
# - `test` - run the test suite in a variety of configurations
#
# - `format` - reformat the code with cargo format
#
# - `clippy` - run clippy with tikv-specific configuration
#
# - `dev` - the rule that needs to pass before submitting a PR. It runs
#   tests and static analysis including clippy and rustfmt
#
# - `release` - create a release profile, optimized build

SHELL := bash
ENABLE_FEATURES ?=

# For CSE, disable unused features
NO_DEFAULT_TEST_ENGINES=1
NO_CLOUD=1

# RocksDB build options (can be overridden by the caller).
#
# `ROCKSDB_SYS_PORTABLE=1` avoids `-march=native` and produces binaries that
# can run across different CPU models of the same architecture (e.g. x86_64
# machines without AVX2).
ROCKSDB_SYS_PORTABLE ?= 1

# Frame pointer is enabled by default. The purpose is to provide stable and
# reliable stack backtraces (for CPU Profiling).
#
# Note that enabling frame-pointer means that the Rust standard library will
# be recompiled.
export RUSTFLAGS := $(RUSTFLAGS) -Cforce-frame-pointers=yes
export CFLAGS := $(CFLAGS) -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer
export CXXFLAGS := $(CXXFLAGS) -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer

# Pick an allocator
ifeq ($(TCMALLOC),1)
ENABLE_FEATURES += tcmalloc
else ifeq ($(MIMALLOC),1)
ENABLE_FEATURES += mimalloc
else ifeq ($(SNMALLOC),1)
ENABLE_FEATURES += snmalloc
else ifeq ($(SYSTEM_ALLOC),1)
# no feature needed for system allocator
else
ENABLE_FEATURES += jemalloc

# Only tested on Linux
ifeq ($(shell uname -s),Linux)
ENABLE_FEATURES += mem-profiling
# According to jemalloc/jemalloc#585, enabling it on some platform or some
# versions of glibc can cause deadlock.
# export JEMALLOC_SYS_WITH_MALLOC_CONF = prof:true,prof_active:false
endif
endif

# Disable portable on macOS to sidestep the compiler bug in clang 4.9
ifeq ($(shell uname -s),Darwin)
ROCKSDB_SYS_PORTABLE=0
RUST_TEST_THREADS ?= 2
endif

# Disable SSE on ARM
ifeq ($(shell uname -p),aarch64)
ROCKSDB_SYS_SSE=0
endif
ifeq ($(shell uname -p),arm)
ROCKSDB_SYS_SSE=0
endif
ifeq ($(shell uname -p),arm64)
ROCKSDB_SYS_SSE=0
endif

# Build portable binary by default unless disable explicitly
ifneq ($(ROCKSDB_SYS_PORTABLE),0)
ENABLE_FEATURES += portable
endif

# Enable sse4.2 by default unless disable explicitly
# Note this env var is also tested by scripts/check-sse4_2.sh
ifneq ($(ROCKSDB_SYS_SSE),0)
ENABLE_FEATURES += sse
endif

ifeq ($(FAIL_POINT),1)
ENABLE_FEATURES += failpoints
endif

ifneq ($(NO_CLOUD),1)
ENABLE_FEATURES += cloud-aws
ENABLE_FEATURES += cloud-gcp
ENABLE_FEATURES += cloud-azure
endif

PROJECT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

BIN_PATH = $(CURDIR)/bin
CARGO_TARGET_DIR ?= $(CURDIR)/target

# Quote a raw Make variable value before embedding it in shell source.
shell_quote = '$(subst ','"'"',$(value $(1)))'

# Build-time environment, captured for reporting by the application binary
BUILD_INFO_GIT_FALLBACK := "Unknown"
BUILD_INFO_RUSTC_FALLBACK := "Unknown"
export TIKV_ENABLE_FEATURES := ${ENABLE_FEATURES}
export TIKV_BUILD_RUSTC_VERSION := $(shell rustc --version 2> /dev/null || echo ${BUILD_INFO_RUSTC_FALLBACK})
export TIKV_BUILD_RUSTC_TARGET := $(shell rustc -vV | awk '/host/ { print $$2 }')
export TIKV_BUILD_GIT_HASH ?= $(shell git rev-parse HEAD 2> /dev/null || echo ${BUILD_INFO_GIT_FALLBACK})
# Replaces all invalid '/' and '_' in the branch name, following https://semver.org/.
ifeq ($(origin TIKV_BUILD_GIT_BRANCH), undefined)
TIKV_BUILD_GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null | tr '/' '.' | tr '_' '-' || echo ${BUILD_INFO_GIT_FALLBACK})
else
override TIKV_BUILD_GIT_BRANCH := $(value TIKV_BUILD_GIT_BRANCH)
endif
export TIKV_BUILD_GIT_BRANCH

# Get cargo package version and Git tags used to derive the build version.
PKG_VERSION := $(shell cargo pkgid | sed 's/.*\#//' | sed 's/.*@//')
GIT_TAG := $(shell git describe --tags --abbrev=0 2> /dev/null)
ifeq ($(origin RELEASE_GIT_TAG), undefined)
ifeq ($(origin TIKV_BUILD_GIT_TAG), undefined)
RELEASE_GIT_TAG := $(shell git describe --tags --exact-match 2> /dev/null)
else
RELEASE_GIT_TAG := $(value TIKV_BUILD_GIT_TAG)
endif
endif
export RELEASE_GIT_TAG
RELEASE_VERSION := $(shell tag=$(call shell_quote,RELEASE_GIT_TAG); \
	printf '%s\n' "$$tag" | sed -nE 's/^v([0-9]+\.[0-9]+\.[0-9]+.*)$$/\1/p')
ifeq ($(RELEASE_VERSION),)
ifeq ($(GIT_TAG),)
override TIKV_BUILD_GIT_TAG := $(PKG_VERSION)
else
override TIKV_BUILD_GIT_TAG := $(shell tag=$(call shell_quote,GIT_TAG); \
	printf '%s\n' "$$tag" | sed 's/^v//')
endif
else
override TIKV_BUILD_GIT_TAG := $(RELEASE_VERSION)
endif
export TIKV_BUILD_GIT_TAG

export DOCKER_IMAGE_NAME ?= "pingcap/tikv"
export DOCKER_IMAGE_TAG ?= "latest"
export TIKV_COMPOSE_BASE_IMAGE ?= hub-zot.pingcap.net/mirrors/tidbx/tikv/tikv/image:release-nextgen-202603
export TIKV_COMPOSE_IMAGE ?= tikv:compose-debug
TIKV_COMPOSE_IMAGE_CONTEXT ?= $(BIN_PATH)/compose-tikv-image

# Turn on cargo pipelining to add more build parallelism. This has shown decent
# speedups in TiKV.
#
# https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199/68
export CARGO_BUILD_PIPELINING=true

# Compiler gave us the following error message when using a specific version of gcc on
# aarch64 architecture and TIKV_FRAME_POINTER=1:
#     .../atomic.rs: undefined reference to __aarch64_xxx
# This is a temporary workaround.
# See: https://github.com/rust-lang/rust/issues/93166
#      https://bugzilla.redhat.com/show_bug.cgi?id=1830472
ifeq ($(TIKV_BUILD_RUSTC_TARGET),aarch64-unknown-linux-gnu)
export RUSTFLAGS := $(RUSTFLAGS) -Ctarget-feature=-outline-atomics
endif

# Almost all the rules in this Makefile are PHONY
# Declaring a rule as PHONY could improve correctness
# But probably instead just improves performance by a little bit
.PHONY: audit check-protobuf clippy format pre-format pre-clippy pre-audit print-build-info unset-override
.PHONY: all build clean dev check-udeps doc error-code fuzz run test
.PHONY: docker docker-tag docker-tag-with-git-hash docker-tag-with-git-tag
.PHONY: build-compose-tikv-binaries build-compose-tikv-image
.PHONY: ctl dist_artifacts dist_tarballs x-build-dist third-party-notices
.PHONY: build_dist_release dist_release dist_unportable_release
.PHONY: fail_release prof_release release unportable_release
.PHONY: pre-test test-build-version test-cloud-engine test-cloud-engine-integration
.PHONY: pre-coverage-report test-with-coverage post-coverage-report coverage-report


default: release

clean:
	cargo clean
	rm -rf bin dist


## Development builds
## ------------------

all: format build test error-code

dev: format clippy
	@env FAIL_POINT=1 make test

build: export TIKV_PROFILE=debug
ifeq ($(TIKV_FRAME_POINTER),1)
build:
	rustup component add rust-src
	cargo build --no-default-features --features "${ENABLE_FEATURES}" \
		-Z build-std=core,std,alloc,proc_macro,test \
		-Z unstable-options \
		--target "${TIKV_BUILD_RUSTC_TARGET}" \
		--out-dir "${CARGO_TARGET_DIR}/debug"
else
build:
	cargo build --no-default-features --features "${ENABLE_FEATURES}"
endif

## Release builds (optimized dev builds)
## ----------------------------

# These builds are heavily optimized, but only use thinLTO, not full
# LTO, and they don't include debuginfo by default.

# An optimized build suitable for development and benchmarking, by default built
# with RocksDB compiled with the "portable" option, for -march=x86-64 (an
# sse2-level instruction set), but with sse4.2 and the PCLMUL instruction
# enabled (the "sse" option)
release: export TIKV_PROFILE=release
ifeq ($(TIKV_FRAME_POINTER),1)
release:
	rustup component add rust-src
	cargo build --release --no-default-features --features "${ENABLE_FEATURES}" \
		-Z build-std=core,std,alloc,proc_macro,test \
		-Z unstable-options \
		--target "${TIKV_BUILD_RUSTC_TARGET}" \
		--out-dir "${CARGO_TARGET_DIR}/release"
else
release:
	cargo build --release --no-default-features --features "${ENABLE_FEATURES}"
endif

# An optimized build that builds an "unportable" RocksDB, which means it is
# built with -march native. It again includes the "sse" option by default.
unportable_release:
	ROCKSDB_SYS_PORTABLE=0 make release

# An optimized build with jemalloc memory profiling enabled.
prof_release:
	ENABLE_FEATURES=mem-profiling make release

# An optimized build instrumented with failpoints.
# This is used for schrodinger chaos testing.
fail_release:
	FAIL_POINT=1 make dist_release

## Distribution builds (true release builds)
## -------------------

# These builds are fully optimized, with LTO, and they contain
# debuginfo. They take a very long time to build, so it is recommended
# not to use them.

# The target used by CI/CD to build the distributable release artifacts.
# Individual developers should only need to use the `dist_` rules when working
# on the CI/CD system.
dist_release:
	make build_dist_release
	@mkdir -p ${BIN_PATH}
	@cp -f ${CARGO_TARGET_DIR}/release/cse-ctl ${CARGO_TARGET_DIR}/release/tikv-server ${BIN_PATH}/
ifeq ($(shell uname),Linux) # Macs binary isn't elf format
	@python scripts/check-bins.py --features "${ENABLE_FEATURES}" --check-release ${BIN_PATH}/cse-ctl ${BIN_PATH}/tikv-server
endif

# Build with release flag as if it were for distribution, but without
# additional sanity checks and file movement.
build_dist_release: export TIKV_PROFILE=dist_release
build_dist_release:
	make x-build-dist
ifeq ($(shell uname),Linux) # Macs don't have objcopy
	# Reduce binary size by compressing binaries.
	# FIXME: Currently errors with `Couldn't find DIE referenced by DW_AT_abstract_origin`
	# dwz ${CARGO_TARGET_DIR}/release/tikv-server
	# FIXME: https://sourceware.org/bugzilla/show_bug.cgi?id=24764
	# dwz ${CARGO_TARGET_DIR}/release/cse-ctl
	objcopy --compress-debug-sections=zlib-gnu ${CARGO_TARGET_DIR}/release/tikv-server
	objcopy --compress-debug-sections=zlib-gnu ${CARGO_TARGET_DIR}/release/cse-ctl
	objcopy --compress-debug-sections=zlib-gnu ${CARGO_TARGET_DIR}/release/tikv-worker
endif

# Distributable bins with SSE4.2 optimizations
dist_unportable_release:
	ROCKSDB_SYS_PORTABLE=0 make dist_release

# Create distributable artifacts. Binaries and Docker image tarballs.
dist_artifacts: dist_tarballs

# Build gzipped tarballs of the binaries and docker images.
# Used to build a `dist/` folder containing the release artifacts.
dist_tarballs: docker
	docker rm -f tikv-binary-extraction-dummy || true
	docker create --name tikv-binary-extraction-dummy pingcap/tikv
	mkdir -p dist bin
	docker cp tikv-binary-extraction-dummy:/tikv-server bin/tikv-server
	docker cp tikv-binary-extraction-dummy:/cse-ctl bin/cse-ctl
	tar -czf dist/tikv.tar.gz bin/*
	docker save pingcap/tikv | gzip > dist/tikv-docker.tar.gz
	docker rm tikv-binary-extraction-dummy

print-build-info:
	@printf '%s\n' "TIKV_BUILD_GIT_HASH: $${TIKV_BUILD_GIT_HASH}"
	@printf '%s\n' "TIKV_BUILD_GIT_BRANCH: $${TIKV_BUILD_GIT_BRANCH}"
	@printf '%s\n' "TIKV_BUILD_GIT_TAG: $${TIKV_BUILD_GIT_TAG}"

third-party-notices:
	@./scripts/generate-third-party-notices.py

# Create tags of the docker images
docker-tag: docker-tag-with-git-hash docker-tag-with-git-tag

# Tag docker images with the git hash
docker-tag-with-git-hash:
	docker tag pingcap/tikv "pingcap/tikv:$${TIKV_BUILD_GIT_HASH}"

# Tag docker images with the git tag
docker-tag-with-git-tag:
	docker tag pingcap/tikv "pingcap/tikv:$${TIKV_BUILD_GIT_TAG}"


## Execution environment
## -------
## Run a command in the environment setup by the Makefile
##
##     COMMAND="echo" make run
##
run:
	@env MAKEFILE_RUN=1 $(COMMAND)

## Testing
## -------

# Run tests under a variety of conditions. This should pass before
# submitting pull requests.
test: test-build-version
	./scripts/test-all -- --nocapture

test-build-version:
	@assert_version() { \
		expected="$$1"; \
		tag="$$2"; \
		branch="$$3"; \
		nearest_tag="$$4"; \
		actual=$$(env -u RELEASE_GIT_TAG -u TIKV_BUILD_GIT_TAG \
			$(MAKE) --no-print-directory print-build-info PKG_VERSION=26.3.99 \
			"GIT_TAG=$$nearest_tag" "TIKV_BUILD_GIT_BRANCH=$$branch" \
			"TIKV_BUILD_GIT_TAG=$$tag" | \
			sed -n 's/^TIKV_BUILD_GIT_TAG: //p'); \
		if [ "$$actual" != "$$expected" ]; then \
			echo "expected build version '$$expected', got '$$actual'"; \
			exit 1; \
		fi; \
	}; \
	assert_recursive_version() { \
		expected="$$1"; \
		tag="$$2"; \
		actual=$$(env -u RELEASE_GIT_TAG -u TIKV_BUILD_GIT_TAG \
			TIKV_BUILD_GIT_TAG="$$tag" $(MAKE) --no-print-directory \
			PKG_VERSION=26.3.99 \
			--eval='test-recursive-build-version: ; @$$(MAKE) --no-print-directory print-build-info PKG_VERSION=26.3.99' \
			test-recursive-build-version | sed -n 's/^TIKV_BUILD_GIT_TAG: //p'); \
		if [ "$$actual" != "$$expected" ]; then \
			echo "expected recursive build version '$$expected', got '$$actual'"; \
			exit 1; \
		fi; \
	}; \
	assert_version 26.3.11 v26.3.11 feature ''; \
	assert_version 26.3.11-cse v26.3.11-cse feature ''; \
	assert_version 26.3.10 26.3.11 feature v26.3.10; \
	assert_version 26.3.10 '' feature v26.3.10; \
	assert_version 26.3.10-cse '' release-26.3 v26.3.10-cse; \
	assert_version 26.3.99 '' HEAD ''; \
	assert_version 26.3.11.1 v26.3.11.1 feature ''; \
	assert_recursive_version 26.3.11 v26.3.11

# Run tests with nextest.
ifndef CUSTOM_TEST_COMMAND
test_with_nextest: export CUSTOM_TEST_COMMAND=nextest run
endif
test_with_nextest: export RUSTDOCFLAGS="-Z unstable-options --persist-doctests"
test_with_nextest:
	./scripts/test-all

## Static analysis
## ---------------

unset-override:
	@# unset first in case of any previous overrides
	@if rustup override list | grep `pwd` > /dev/null; then rustup override unset; fi

check-protobuf:
	@cargo build -p kvenginepb
	@cargo build -p rfenginepb
	@if ! git diff --quiet; then \
		echo "Error: There are uncommitted protobuf changes"; \
		git --no-pager diff; \
		exit 1; \
	fi

pre-format: unset-override
	@rustup component add rustfmt
	@which cargo-sort &> /dev/null || cargo install -q cargo-sort@1.0.9 --locked

format: pre-format
	@cargo fmt
	@cargo sort -w >/dev/null

doc:
	@cargo doc --workspace --document-private-items \
		--exclude fuzz-targets --exclude fuzzer-honggfuzz --exclude fuzzer-afl --exclude fuzzer-libfuzzer \
		--no-default-features --features "${ENABLE_FEATURES}"

pre-clippy: unset-override
	@rustup component add clippy

clippy: pre-clippy
	@./scripts/check-redact-log
	@./scripts/check-dashboards
	@./scripts/check-docker-build
	@./scripts/check-license
	@./scripts/check-workspace-hack
	@./scripts/clippy-all

pre-audit:
	$(eval LATEST_AUDIT_VERSION := $(strip $(shell cargo search cargo-audit | head -n 1 | awk '{ gsub(/"/, "", $$3); print $$3 }')))
	$(eval CURRENT_AUDIT_VERSION = $(strip $(shell (cargo audit --version 2> /dev/null || echo "noop 0") | awk '{ print $$2 }')))
	@if [ "$(LATEST_AUDIT_VERSION)" != "$(CURRENT_AUDIT_VERSION)" ]; then \
		cargo install cargo-audit --force; \
	fi

# Check for security vulnerabilities
audit: pre-audit
	cargo audit

check-udeps:
	(which cargo-udeps &>/dev/null || cargo install cargo-udeps) && cargo udeps

FUZZER ?= Honggfuzz

fuzz:
	@cargo run --package fuzz --no-default-features --features "${ENABLE_FEATURES}" -- run ${FUZZER} ${FUZZ_TARGET} \
	|| echo "" && echo "Set the target for fuzzing using FUZZ_TARGET and the fuzzer using FUZZER (default is Honggfuzz)"

## Special targets
## ---------------

# A special target for building just the cse-ctl binary and release mode and copying it
# into BIN_PATH. It's not clear who uses this for what. If you know please document it.
ctl:
	cargo build --release --no-default-features --features "${ENABLE_FEATURES}" --bin cse-ctl
	@mkdir -p ${BIN_PATH}
	@cp -f ${CARGO_TARGET_DIR}/release/cse-ctl ${BIN_PATH}/

# Actually use make to track dependencies! This saves half a second.
error_code_files := $(shell find $(PROJECT_DIR)/components/error_code/ -type f )
etc/error_code.toml: $(error_code_files)
	cargo run --manifest-path components/error_code/Cargo.toml

error-code: etc/error_code.toml

# A special target for building TiKV docker image.
docker:
	docker build \
		-t ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} \
		--build-arg "GIT_HASH=$${TIKV_BUILD_GIT_HASH}" \
		--build-arg "GIT_TAG=$${TIKV_BUILD_GIT_TAG}" \
		--build-arg "GIT_BRANCH=$${TIKV_BUILD_GIT_BRANCH}" \
		.

# Build debug TiKV binaries for quick local Docker Compose validation.
build-compose-tikv-binaries: export TIKV_PROFILE=debug
ifeq ($(TIKV_FRAME_POINTER),1)
build-compose-tikv-binaries:
	rustup component add rust-src
	cargo build --no-default-features --features "${ENABLE_FEATURES}" \
		--bin tikv-server \
		--bin tikv-worker \
		-Z build-std=core,std,alloc,proc_macro,test \
		-Z unstable-options \
		--target "${TIKV_BUILD_RUSTC_TARGET}" \
		--out-dir "${CARGO_TARGET_DIR}/debug"
else
build-compose-tikv-binaries:
	cargo build --no-default-features --features "${ENABLE_FEATURES}" --bin tikv-server --bin tikv-worker
endif

# Build a local TiKV image for Docker Compose by replacing /tikv-server and
# /tikv-worker in the release-nextgen base image.
build-compose-tikv-image: build-compose-tikv-binaries
	@test -x "${CARGO_TARGET_DIR}/debug/tikv-server"
	@test -x "${CARGO_TARGET_DIR}/debug/tikv-worker"
	@rm -rf "${TIKV_COMPOSE_IMAGE_CONTEXT}"
	@mkdir -p "${TIKV_COMPOSE_IMAGE_CONTEXT}"
	@cp -f "${CARGO_TARGET_DIR}/debug/tikv-server" "${TIKV_COMPOSE_IMAGE_CONTEXT}/tikv-server"
	@cp -f "${CARGO_TARGET_DIR}/debug/tikv-worker" "${TIKV_COMPOSE_IMAGE_CONTEXT}/tikv-worker"
	@cp -f scripts/docker-compose/Dockerfile.tikv-binaries "${TIKV_COMPOSE_IMAGE_CONTEXT}/Dockerfile"
	docker build \
		--build-arg BASE_IMAGE="${TIKV_COMPOSE_BASE_IMAGE}" \
		--build-arg "GIT_HASH=$${TIKV_BUILD_GIT_HASH}" \
		--build-arg "GIT_TAG=$${TIKV_BUILD_GIT_TAG}" \
		--build-arg "GIT_BRANCH=$${TIKV_BUILD_GIT_BRANCH}" \
		-t "${TIKV_COMPOSE_IMAGE}" \
		"${TIKV_COMPOSE_IMAGE_CONTEXT}"
	@echo "Built ${TIKV_COMPOSE_IMAGE} from ${TIKV_COMPOSE_BASE_IMAGE}"
	@echo "Use with compose: cd scripts/docker-compose && export TIKV_COMPOSE_IMAGE=${TIKV_COMPOSE_IMAGE} && docker compose down && docker compose up -d"

## The driver for script/run-cargo.sh
## ----------------------------------

# Cargo only has two non-test profiles, dev and release, and we have
# more than two use cases for which a cargo profile is required. This
# is a hack to manage more cargo profiles, written in `etc/cargo.config.*`.
# So we use cargo `config-profile` feature to specify profiles in
# `.cargo/config`, which `scripts/run-cargo.sh copies into place.
#
# Presently the only thing this is used for is the `dist_release`
# rules, which are used for producing release builds.

DIST_CONFIG=etc/cargo.config.dist

ifneq ($(DEBUG),)
export X_DEBUG=${DEBUG}
endif

export X_CARGO_ARGS:=${CARGO_ARGS}

x-build-dist: export X_CARGO_CMD=build
x-build-dist: export X_CARGO_FEATURES=${ENABLE_FEATURES}
x-build-dist: export X_CARGO_RELEASE=1
x-build-dist: export X_CARGO_CONFIG_FILE=${DIST_CONFIG}
x-build-dist: export X_CARGO_TARGET_DIR=${CARGO_TARGET_DIR}
x-build-dist: export X_PACKAGE=tikv-server cse-ctl tikv-worker
x-build-dist:
	bash scripts/run-cargo.sh

# Build with debug flag as if it were for distribution, but without
# additional sanity checks and file movement.
build_dist_debug: export TIKV_PROFILE=dist_debug
build_dist_debug:
	make x-build-dist-debug
ifeq ($(shell uname),Linux) # Macs don't have objcopy
	# Reduce binary size by compressing binaries.
	# FIXME: Currently errors with `Couldn't find DIE referenced by DW_AT_abstract_origin`
	# dwz ${CARGO_TARGET_DIR}/debug/tikv-server
	# FIXME: https://sourceware.org/bugzilla/show_bug.cgi?id=24764
	# dwz ${CARGO_TARGET_DIR}/debug/cse-ctl
	objcopy --compress-debug-sections=zlib-gnu ${CARGO_TARGET_DIR}/debug/tikv-server
	objcopy --compress-debug-sections=zlib-gnu ${CARGO_TARGET_DIR}/debug/cse-ctl
endif

x-build-dist-debug: export X_CARGO_CMD=build
x-build-dist-debug: export X_CARGO_FEATURES=${ENABLE_FEATURES}
x-build-dist-debug: export X_CARGO_RELEASE=0
x-build-dist-debug: export X_CARGO_CONFIG_FILE=${DIST_CONFIG}
x-build-dist-debug: export X_CARGO_TARGET_DIR=${CARGO_TARGET_DIR}
x-build-dist-debug: export X_PACKAGE=tikv-server cse-ctl tikv-worker
x-build-dist-debug:
	bash scripts/run-cargo.sh

pre-test:
	@which cargo-nextest &> /dev/null || cargo install -q cargo-nextest@0.9.85 --locked

UNIT_TEST_PACKAGES := \
	tikv kvengine rfstore rfengine \
	cse-ctl tikv-worker cloud_worker \
	test_cloud_server test_pd_client \
	native_br load_data cloud_encryption \
	cloud_server pd_client api_version \
	concurrency_manager cdc \
	trace_event txn_types \
	merged_engine replication_worker
UNIT_TEST_PACKAGES_STR := $(foreach p,$(UNIT_TEST_PACKAGES),-p $(p))

# UNIT_TEST_PACKAGES crates that declare a `failpoints` feature. Read the package
# name from Cargo.toml because it isn't required to match the crate directory.
FAILPOINT_CRATES := $(shell for f in $$(grep -rlE '^failpoints[[:space:]]*=' --include=Cargo.toml components cmd); do \
	sed -n 's/^name[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$$f" | head -n 1; \
	done)
UNIT_TEST_FAILPOINT_FEATURES := $(foreach c,$(filter $(FAILPOINT_CRATES),$(UNIT_TEST_PACKAGES)),$(c)/failpoints)
UNIT_TEST_FEATURES := $(UNIT_TEST_FAILPOINT_FEATURES) tikv/testexport

test-cloud-engine: pre-test
	cargo nextest run -P ci \
		$(UNIT_TEST_PACKAGES_STR) \
		--features "$(UNIT_TEST_FEATURES)" \
		--tests

test-cloud-engine-integration: pre-test
	cargo nextest run -P ci -p tests \
		--test cloud_engine \
		--test cloud_engine_failpoints \
		--test-threads=4

coverage-report: pre-coverage-report test-with-coverage post-coverage-report

prepare-coverage-tooling: pre-test pre-coverage-report

pre-coverage-report:
	@which cargo-llvm-cov &> /dev/null || cargo install -q cargo-llvm-cov@0.6.15 --locked
	cargo llvm-cov clean --workspace

test-with-coverage: pre-test collect-unit-coverage-data collect-integration-coverage-data collect-random-coverage-data

collect-unit-coverage-data:
	cargo llvm-cov nextest -P coverage \
		$(UNIT_TEST_PACKAGES_STR) \
		--features "$(UNIT_TEST_FAILPOINT_FEATURES)" \
		--tests \
		--no-report

collect-integration-coverage-data:
	cargo llvm-cov nextest -P coverage -p tests \
		--test cloud_engine --test cloud_engine_failpoints \
		--test-threads=4 \
		--no-report

collect-random-coverage-data:
	LOG_FILE=$(CURDIR)/random_test.log \
		cargo llvm-cov nextest -P random -p tests \
		--test random test_random_all \
		--no-report

COVERAGE_OUTPUT_DIR ?= coverage
post-coverage-report:
	cargo llvm-cov report --html --output-dir $(COVERAGE_OUTPUT_DIR)/
	cargo llvm-cov report --lcov --output-path $(COVERAGE_OUTPUT_DIR)/lcov.info
test-cloud-engine-integration-with-nextest:
	cargo nextest run -P ci -p tests --test cloud_engine --test cloud_engine_failpoints -j 2
