# Makefile

# SPDX-License-Identifier: MIT
# Copyright (c) The leveldb-rs-binding authors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

EXTENSION_SRC_DIR := extension/src
EXTENSION_TEST_DIR := extension/tests
EXTENSION_INCLUDE_DIR := extension/include
EXTENSION_SRC_FILES := $(shell find $(EXTENSION_SRC_DIR) -name "*.cpp")
EXTENSION_SRC_HEADER_FILES := $(shell find $(EXTENSION_SRC_DIR) -name "*.hpp")
EXTENSION_TEST_FILES := $(shell find $(EXTENSION_TEST_DIR) -name "*.cpp")
EXTENSION_INCLUDE_FILES := $(shell find $(EXTENSION_INCLUDE_DIR) -name "*.h")

.PHONY: lint
lint: format fix clippy clang-tidy

.PHONY: clippy
clippy:
	cargo clippy --features linux -- -D warnings

.PHONY: clippy-src
clippy-src:
	cargo clippy --manifest-path Cargo.toml --features linux -- -D warnings

.PHONY: clippy-benchmarks
clippy-benchmarks:
	cargo clippy --manifest-path benchmarks/Cargo.toml -- -D warnings

.PHONY: clippy-example
clippy-example:
	cargo clippy --manifest-path example/http_server/Cargo.toml -- -D warnings
	cargo clippy --manifest-path example/simple_app/Cargo.toml -- -D warnings

.PHONY: clang-tidy
clang-tidy:
	@BOOST_INC_DIRS=$$(find deps/boost/libs -maxdepth 3 -type d -name include 2>/dev/null | sed 's|^|-I./|'); \
	clang-tidy $(EXTENSION_SRC_HEADER_FILES) $(EXTENSION_SRC_FILES) $(EXTENSION_TEST_FILES) \
		--extra-arg=-std=c++20 --warnings-as-errors='*' \
		-- -I./extension -I./extension/src -I./deps/leveldb/include -I./deps/t3lru/include \
		$$BOOST_INC_DIRS \
		-DRUST_USIZE_SIZE=8 -DBUILD_TESTS=1

.PHONY: format
format: format-src format-benchmarks format-example format-extension

.PHONY: format-src
format-src:
	cargo fmt --manifest-path Cargo.toml

.PHONY: format-benchmarks
format-benchmarks:
	cargo fmt --manifest-path benchmarks/Cargo.toml

.PHONY: format-example
format-example:
	cargo fmt --manifest-path example/http_server/Cargo.toml
	cargo fmt --manifest-path example/simple_app/Cargo.toml

.PHONY: format-extension
format-extension:
	clang-format -i $(EXTENSION_SRC_HEADER_FILES) $(EXTENSION_SRC_FILES) $(EXTENSION_TEST_FILES) $(EXTENSION_INCLUDE_FILES)

.PHONY: fix
fix: fix-src fix-benchmarks fix-example

.PHONY: fix-src
fix-src:
	cargo fix --all-targets --allow-dirty --manifest-path Cargo.toml

.PHONY: fix-benchmarks
fix-benchmarks:
	cargo fix --all-targets --allow-dirty --manifest-path benchmarks/Cargo.toml

.PHONY: fix-example
fix-example:
	cargo fix --all-targets --allow-dirty --manifest-path example/http_server/Cargo.toml
	cargo fix --all-targets --allow-dirty --manifest-path example/simple_app/Cargo.toml

# Directory-specific lint targets
.PHONY: lint-src
lint-src: format-src fix-src clippy-src

.PHONY: lint-benchmarks
lint-benchmarks: format-benchmarks fix-benchmarks clippy-benchmarks

.PHONY: lint-example
lint-example: format-example fix-example clippy-example

.PHONY: lint-extension
lint-extension: format-extension clang-tidy

# Extension build and test targets
.PHONY: configure-extension-lib-boost
configure-extension-lib-boost:
	@echo "Configuring Boost dependency library ..."
	@cd deps/boost && \
	git submodule update --depth 1 -q --init tools/boostdep && \
	git submodule update --depth 1 -q --init libs/unordered && \
	python3 tools/boostdep/depinst/depinst.py -v -X test -g "--depth 1" unordered

# Library build targets
.PHONY: build-extension-lib-leveldb
build-extension-lib-leveldb:
	@echo "Building LevelDB dependency library ($(BUILD_TYPE)) ..."
	@mkdir -p extension/build/leveldb/$(BUILD_TYPE)
	@cd extension/build/leveldb/$(BUILD_TYPE) && \
	cmake ../../../../deps/leveldb \
		-DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \
		-DCMAKE_CXX_STANDARD=20 \
		-DCMAKE_CXX_STANDARD_REQUIRED=ON \
		-DCMAKE_C_STANDARD=11 \
		-DCMAKE_C_STANDARD_REQUIRED=ON \
		-DCMAKE_CXX_FLAGS="$(EXTRA_CXX_FLAGS)" \
		-DCMAKE_INSTALL_PREFIX="../../.." \
		-DCMAKE_INSTALL_LIBDIR=build/lib/$(BUILD_TYPE) \
		-DCMAKE_INSTALL_INCLUDEDIR=build/lib/$(BUILD_TYPE)/include \
		-DLEVELDB_BUILD_TESTS=OFF \
		-DHAVE_SNAPPY=OFF \
		-DHAVE_ZSTD=OFF \
		-DLEVELDB_BUILD_BENCHMARKS=OFF && \
	cmake --build . && \
	cmake --install .

.PHONY: build-extension-lib-t3lru
build-extension-lib-t3lru:
	@echo "Building t3lru dependency library ($(BUILD_TYPE)) ..."
	@mkdir -p extension/build/t3lru/$(BUILD_TYPE)
	@cd extension/build/t3lru/$(BUILD_TYPE) && \
	cmake ../../../../deps/t3lru \
		-DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \
		-DCMAKE_CXX_STANDARD=20 \
		-DCMAKE_CXX_FLAGS="$(EXTRA_CXX_FLAGS)" \
		-DCMAKE_INSTALL_PREFIX="../../../build/lib/$(BUILD_TYPE)" \
		-DCMAKE_INSTALL_LIBDIR=lib \
		-DCMAKE_INSTALL_INCLUDEDIR=include && \
	cmake --build . && \
	cmake --install .

.PHONY: build-extension-lib-boost
build-extension-lib-boost:
	@echo "Building Boost dependency library ($(BUILD_TYPE)) ..."
	@mkdir -p extension/build/boost/$(BUILD_TYPE)
	@cd extension/build/boost/$(BUILD_TYPE) && \
	cmake ../../../../deps/boost \
		-DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \
		-DCMAKE_CXX_STANDARD=20 \
		-DCMAKE_CXX_FLAGS="$(EXTRA_CXX_FLAGS)" \
		-DCMAKE_INSTALL_PREFIX="../../.." \
		-DCMAKE_INSTALL_LIBDIR=build/lib/$(BUILD_TYPE) \
		-DCMAKE_INSTALL_INCLUDEDIR=build/lib/$(BUILD_TYPE)/include && \
	cmake --build . && \
	cmake --install .

.PHONY: build-extension-libs
build-extension-libs: build-extension-lib-leveldb build-extension-lib-boost build-extension-lib-t3lru

# Test build targets
.PHONY: build-extension-test
build-extension-test:
	@echo "Building extension tests ..."
	$(MAKE) BUILD_TYPE=Debug EXTRA_CXX_FLAGS="-fsanitize=thread" build-extension-libs
	@mkdir -p extension/build/test
	@cd extension/build/test && \
	cmake ../.. \
	    -DCMAKE_BUILD_TYPE=Debug \
		-DCMAKE_CXX_STANDARD_REQUIRED=ON \
		-DCMAKE_CXX_FLAGS="-fsanitize=thread" \
		-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \
		-DLEVELDB_INSTALL_DIR=../../build/lib/Debug \
		-DT3LRU_INSTALL_DIR=../../build/lib/Debug \
		-DBUILD_TESTS=ON && \
	cmake --build . --target extension_tests

# Benchmark build targets
.PHONY: build-extension-bench
build-extension-bench:
	@echo "Building extension benchmarks ..."
	$(MAKE) BUILD_TYPE=Release EXTRA_CXX_FLAGS="" build-extension-libs
	@mkdir -p extension/build/bench
	@cd extension/build/bench && \
	cmake ../.. \
		-DCMAKE_BUILD_TYPE=Release \
		-DCMAKE_CXX_STANDARD_REQUIRED=ON \
		-DLEVELDB_INSTALL_DIR=../../build/lib/Release \
		-DT3LRU_INSTALL_DIR=../../build/lib/Release \
		-DBUILD_BENCHMARKS=ON && \
	cmake --build . --target extension_benchmarks

# Clean targets
.PHONY: clean-extension-test
clean-extension-test:
	@echo "Cleaning extension tests ..."
	@rm -rf extension/build/test

.PHONY: clean-extension-bench
clean-extension-bench:
	@echo "Cleaning extension benchmarks ..."
	@rm -rf extension/build/bench

.PHONY: clean-extension-build
clean-extension-build:
	@echo "Cleaning extension build dir ..."
	@rm -rf extension/build

# Run targets
.PHONY: run-extension-test
run-extension-test:
	@echo "Running extension tests ..."
	@if ! ls extension/build/test/*_test 2>/dev/null | grep -q .; then \
		echo "Error: No test executable found. Run 'make build-extension-test' first."; \
		exit 1; \
	fi
	@cd extension/build/test && ctest --output-on-failure

.PHONY: run-extension-bench
run-extension-bench:
	@echo "Running extension benchmarks ..."
	@if ! ls extension/build/bench/*_bench 2>/dev/null | grep -q .; then \
		echo "Error: No benchmark executable found. Run 'make build-extension-bench' first."; \
		exit 1; \
	fi
	@cd extension/build/bench && for bench in ./*_bench; do echo "--- $$bench ---" && ./$$bench; done
