# retoor CC = gcc CFLAGS = -Wall -Wextra -Werror -O3 -march=native -flto=auto -fomit-frame-pointer -D_GNU_SOURCE CFLAGS_DEBUG = -Wall -Wextra -Werror -O0 -g -D_GNU_SOURCE CFLAGS_COV = -Wall -Wextra -Werror -O0 -g -D_GNU_SOURCE --coverage -fprofile-arcs -ftest-coverage LDFLAGS = -flto -lssl -lcrypto -lsqlite3 -lm -lpthread LDFLAGS_COV = -lssl -lcrypto -lsqlite3 -lm -lpthread --coverage SRC_DIR = src BUILD_DIR = build TESTS_DIR = tests SOURCES = $(SRC_DIR)/main.c \ $(SRC_DIR)/buffer.c \ $(SRC_DIR)/logging.c \ $(SRC_DIR)/config.c \ $(SRC_DIR)/config_parser.c \ $(SRC_DIR)/monitor.c \ $(SRC_DIR)/http.c \ $(SRC_DIR)/http_response.c \ $(SRC_DIR)/ssl_handler.c \ $(SRC_DIR)/connection.c \ $(SRC_DIR)/client_handler.c \ $(SRC_DIR)/upstream.c \ $(SRC_DIR)/forwarding.c \ $(SRC_DIR)/dashboard.c \ $(SRC_DIR)/rate_limit.c \ $(SRC_DIR)/auth.c \ $(SRC_DIR)/base64.c \ $(SRC_DIR)/health_check.c \ $(SRC_DIR)/patch.c \ $(SRC_DIR)/socket_utils.c \ $(SRC_DIR)/epoll_utils.c \ $(SRC_DIR)/time_utils.c \ $(SRC_DIR)/histogram.c \ $(SRC_DIR)/deque.c \ $(SRC_DIR)/rate_tracker.c \ $(SRC_DIR)/stats_collector.c \ cJSON.c OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(SOURCES))) TARGET = rproxy TEST_SOURCES = $(TESTS_DIR)/test_main.c \ $(TESTS_DIR)/test_http.c \ $(TESTS_DIR)/test_buffer.c \ $(TESTS_DIR)/test_config.c \ $(TESTS_DIR)/test_routing.c \ $(TESTS_DIR)/test_host_rewrite.c \ $(TESTS_DIR)/test_http_helpers.c \ $(TESTS_DIR)/test_patch.c \ $(TESTS_DIR)/test_auth.c \ $(TESTS_DIR)/test_rate_limit.c \ $(TESTS_DIR)/test_monitor.c \ $(TESTS_DIR)/test_dashboard.c \ $(TESTS_DIR)/test_health_check.c \ $(TESTS_DIR)/test_ssl_handler.c \ $(TESTS_DIR)/test_connection.c \ $(TESTS_DIR)/test_logging.c TEST_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_SOURCES))) TEST_LIB_SOURCES = $(SRC_DIR)/buffer.c \ $(SRC_DIR)/logging.c \ $(SRC_DIR)/config.c \ $(SRC_DIR)/config_parser.c \ $(SRC_DIR)/monitor.c \ $(SRC_DIR)/http.c \ $(SRC_DIR)/http_response.c \ $(SRC_DIR)/ssl_handler.c \ $(SRC_DIR)/connection.c \ $(SRC_DIR)/client_handler.c \ $(SRC_DIR)/upstream.c \ $(SRC_DIR)/forwarding.c \ $(SRC_DIR)/dashboard.c \ $(SRC_DIR)/rate_limit.c \ $(SRC_DIR)/auth.c \ $(SRC_DIR)/base64.c \ $(SRC_DIR)/health_check.c \ $(SRC_DIR)/patch.c \ $(SRC_DIR)/socket_utils.c \ $(SRC_DIR)/epoll_utils.c \ $(SRC_DIR)/time_utils.c \ $(SRC_DIR)/histogram.c \ $(SRC_DIR)/deque.c \ $(SRC_DIR)/rate_tracker.c \ $(SRC_DIR)/stats_collector.c \ cJSON.c TEST_LIB_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_LIB_SOURCES))) TEST_TARGET = rproxy_test MIN_COVERAGE = 60 COVERAGE_MODULES = auth.c buffer.c config.c http.c logging.c patch.c rate_limit.c monitor.c dashboard.c health_check.c ssl_handler.c connection.c .PHONY: all clean test legacy run coverage coverage-html valgrind all: $(BUILD_DIR) $(TARGET) $(BUILD_DIR): mkdir -p $(BUILD_DIR) $(TARGET): $(OBJECTS) $(CC) $(OBJECTS) -o $@ $(LDFLAGS) $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/cJSON.o: cJSON.c | $(BUILD_DIR) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/test_%.o: $(TESTS_DIR)/test_%.c | $(BUILD_DIR) $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(TEST_TARGET): $(BUILD_DIR) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) $(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $@ $(LDFLAGS) test: $(TEST_TARGET) ./$(TEST_TARGET) legacy: rproxy.c cJSON.c cJSON.h $(CC) $(CFLAGS) rproxy.c cJSON.c -o rproxy_legacy $(LDFLAGS) run: $(TARGET) ./$(TARGET) coverage: clean mkdir -p $(BUILD_DIR) @for src in $(TEST_LIB_SOURCES); do \ obj=$(BUILD_DIR)/$$(basename $${src%.c}.o); \ $(CC) $(CFLAGS_COV) -c $$src -o $$obj; \ done @for src in $(TEST_SOURCES); do \ obj=$(BUILD_DIR)/$$(basename $${src%.c}.o); \ $(CC) $(CFLAGS_COV) -I$(SRC_DIR) -c $$src -o $$obj; \ done $(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $(TEST_TARGET) $(LDFLAGS_COV) ./$(TEST_TARGET) @echo "" @echo "=== Coverage Report ===" @gcov -n build/*.gcno 2>/dev/null | grep -A 1 "^File.*src/" | grep -v "^--$$" || true @echo "" @echo "=== Coverage Check (minimum $(MIN_COVERAGE)%) ===" @echo "Checking modules: $(COVERAGE_MODULES)" @total_lines=0; covered_lines=0; \ for gcno in build/*.gcno; do \ output=$$(gcov -n "$$gcno" 2>/dev/null); \ file=$$(echo "$$output" | grep "^File.*src/" | head -1 | sed "s/File '\\(.*\\)'/\\1/"); \ if [ -n "$$file" ]; then \ basename=$$(basename "$$file"); \ is_tracked=0; \ for mod in $(COVERAGE_MODULES); do \ if [ "$$basename" = "$$mod" ]; then is_tracked=1; break; fi; \ done; \ if [ $$is_tracked -eq 1 ]; then \ lines=$$(echo "$$output" | grep "Lines executed:" | head -1 | sed 's/.*of \([0-9]*\)/\1/'); \ pct=$$(echo "$$output" | grep "Lines executed:" | head -1 | sed 's/Lines executed:\([0-9.]*\)%.*/\1/'); \ if [ -n "$$lines" ] && [ -n "$$pct" ]; then \ covered=$$(echo "$$pct * $$lines / 100" | bc -l | cut -d. -f1); \ covered=$${covered:-0}; \ total_lines=$$((total_lines + lines)); \ covered_lines=$$((covered_lines + covered)); \ echo " $$basename: $$pct% ($$covered/$$lines lines)"; \ fi; \ fi; \ fi; \ done; \ if [ $$total_lines -gt 0 ]; then \ avg=$$(echo "scale=2; $$covered_lines * 100 / $$total_lines" | bc -l); \ echo ""; \ echo "Total: $$covered_lines/$$total_lines lines covered ($$avg%)"; \ avg_int=$$(echo "$$avg" | cut -d. -f1); \ if [ $$avg_int -lt $(MIN_COVERAGE) ]; then \ echo "FAILED: Coverage $$avg% is below minimum $(MIN_COVERAGE)%"; \ exit 1; \ else \ echo "PASSED: Coverage $$avg% meets minimum $(MIN_COVERAGE)%"; \ fi; \ else \ echo "FAILED: No coverage data found"; \ exit 1; \ fi coverage-html: coverage mkdir -p coverage_report lcov --capture --directory build --output-file coverage_report/coverage.info --ignore-errors source 2>/dev/null || true genhtml coverage_report/coverage.info --output-directory coverage_report 2>/dev/null || echo "Install lcov for HTML reports: sudo apt install lcov" @echo "Coverage report generated in coverage_report/index.html" valgrind: clean mkdir -p $(BUILD_DIR) @for src in $(TEST_LIB_SOURCES); do \ obj=$(BUILD_DIR)/$$(basename $${src%.c}.o); \ $(CC) $(CFLAGS_DEBUG) -c $$src -o $$obj; \ done @for src in $(TEST_SOURCES); do \ obj=$(BUILD_DIR)/$$(basename $${src%.c}.o); \ $(CC) $(CFLAGS_DEBUG) -I$(SRC_DIR) -c $$src -o $$obj; \ done $(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $(TEST_TARGET) -lssl -lcrypto -lsqlite3 -lm -lpthread valgrind --leak-check=full --show-leak-kinds=definite,indirect --error-exitcode=1 ./$(TEST_TARGET) 2>&1 | tee valgrind.log; \ VALGRIND_EXIT=$$?; \ echo ""; \ echo "=== Valgrind Summary ==="; \ grep -E "(definitely|indirectly) lost:" valgrind.log || true; \ grep "All heap blocks were freed" valgrind.log || true; \ grep "ERROR SUMMARY:" valgrind.log || true; \ if [ $$VALGRIND_EXIT -ne 0 ]; then \ echo "FAILED: Valgrind detected errors"; rm -f valgrind.log; exit 1; \ fi; \ if grep -q "definitely lost: [1-9]" valgrind.log; then \ echo "FAILED: Definite memory leaks detected"; rm -f valgrind.log; exit 1; \ fi; \ if grep -q "indirectly lost: [1-9]" valgrind.log; then \ echo "FAILED: Indirect memory leaks detected"; rm -f valgrind.log; exit 1; \ fi; \ rm -f valgrind.log @echo "" @echo "=== Valgrind Check Passed ===" clean: rm -rf $(BUILD_DIR) $(TARGET) $(TEST_TARGET) rproxy_legacy *.gcov coverage_report