# jerboa-sqlite -- a SQLite3-compatible database engine in Jerboa Scheme.
#
# jerbuild bundles Chez Scheme + the jerboa stdlib, so building jsqlite needs
# only `jerbuild` on PATH. The differential ("diff") test lane additionally
# needs the jerboa-sqlite reference (a thin FFI over the system libsqlite3),
# checked out as a sibling directory; it is TEST-ONLY (see the working rule in
# OPUS_4_8_SQLITE_HANDOFF.md: never call libsqlite3 from production modules).

JERBUILD ?= jerbuild
JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null)
ifeq ($(JH),)
$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild)
endif

SRC_DIR   := $(CURDIR)/src
TOOLS_DIR := $(CURDIR)/tools
REF_DIR   := $(CURDIR)/../jerboa-sqlite-ffi
REF_LIB   := $(REF_DIR)/lib

# Production execution: just our source + the jerboa stdlib.
LIBDIRS      := $(SRC_DIR):$(JH)/lib
# Differential execution: also the test tools + the reference oracle library.
DIFF_LIBDIRS := $(SRC_DIR):$(TOOLS_DIR):$(REF_LIB):$(JH)/lib

UNIT_TESTS := $(wildcard tests/unit/*.ss)
DIFF_TESTS := $(wildcard tests/diff/*.ss)

.PHONY: all build unit diff test oracle fuzz corrupt dmlfuzz concurrency cshim bench robustness clean help
.DEFAULT_GOAL := help

all: test

# Compile-check: import every production module (R6RS libraries in .ss files
# are consumed directly via libdirs, so there is no transpile step).
build:
	$(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/build-check.ss

# Unit tests run directly against src/ (jerbuild exec compiles on the fly).
unit:
	@fail=0; for t in $(UNIT_TESTS); do \
	  echo "== unit: $$t =="; \
	  $(JERBUILD) exec --libdirs "$(LIBDIRS)" $$t || fail=1; \
	done; \
	if [ $$fail -ne 0 ]; then echo "UNIT FAILURES"; exit 1; fi

# Build the reference oracle (shim + transpiled lib) for differential tests.
oracle:
	@if [ ! -d "$(REF_DIR)" ]; then \
	  echo "reference $(REF_DIR) not found"; exit 1; fi
	$(MAKE) -C $(REF_DIR) build

# Differential tests compare jsqlite against the reference engine. They are
# skipped (not failed) when the reference checkout is unavailable.
diff:
	@if [ ! -d "$(REF_DIR)" ]; then \
	  echo "SKIP diff: reference $(REF_DIR) not found"; exit 0; fi; \
	$(MAKE) --no-print-directory -C $(REF_DIR) build >/dev/null || \
	  { echo "SKIP diff: reference build failed"; exit 0; }; \
	fail=0; for t in $(DIFF_TESTS); do \
	  echo "== diff: $$t =="; \
	  DYLD_LIBRARY_PATH=$(REF_DIR) LD_LIBRARY_PATH=$(REF_DIR) \
	  $(JERBUILD) exec --libdirs "$(DIFF_LIBDIRS)" $$t || fail=1; \
	done; \
	if [ $$fail -ne 0 ]; then echo "DIFF FAILURES"; exit 1; fi

test: unit diff

# Robustness tools (not part of the default gate; longer-running, explicit).
# Override count/seed with `make fuzz N=5000 SEED=7`.
fuzz:
	@if [ ! -d "$(REF_DIR)" ]; then echo "SKIP fuzz: reference $(REF_DIR) not found"; exit 0; fi; \
	$(MAKE) --no-print-directory -C $(REF_DIR) build >/dev/null || \
	  { echo "SKIP fuzz: reference build failed"; exit 0; }; \
	DYLD_LIBRARY_PATH=$(REF_DIR) LD_LIBRARY_PATH=$(REF_DIR) \
	$(JERBUILD) exec --libdirs "$(DIFF_LIBDIRS)" tools/fuzz.ss $(N) $(SEED)

# Malformed-image corpus test; needs only jsqlite (no oracle).
corrupt:
	$(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/corrupt-test.ss $(N) $(SEED)

# Multi-process write-locking stress test: 4 processes hammer one file, then
# verify no writes were lost. Needs only jsqlite (no oracle). `make concurrency N=40`.
concurrency:
	@P=/tmp/jsqlite-concurrency.db; n=$${N:-40}; \
	for mode in "" wal; do \
	  echo "== concurrency (journal mode: $${mode:-rollback}) =="; \
	  rm -f $$P $$P-lock $$P-journal $$P-wal $$P-shm; \
	  $(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/concurrency-test.ss init $$P; \
	  for id in 1 2 3 4; do \
	    $(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/concurrency-test.ss worker $$id $$n $$P $$mode >/dev/null & \
	  done; wait; \
	  $(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/concurrency-test.ss verify $$P 4 $$n || exit 1; \
	done

# Write-path / B-tree property test vs the oracle (`make dmlfuzz N=2000 SEED=7`).
dmlfuzz:
	@if [ ! -d "$(REF_DIR)" ]; then echo "SKIP dmlfuzz: reference $(REF_DIR) not found"; exit 0; fi; \
	$(MAKE) --no-print-directory -C $(REF_DIR) build >/dev/null || \
	  { echo "SKIP dmlfuzz: reference build failed"; exit 0; }; \
	DYLD_LIBRARY_PATH=$(REF_DIR) LD_LIBRARY_PATH=$(REF_DIR) \
	$(JERBUILD) exec --libdirs "$(DIFF_LIBDIRS)" tools/dml-fuzz.ss $(N) $(SEED)

# C ABI shim: compile the C driver (shim/driver.c) and have it drive jsqlite
# prepared statements through the jsq_api function-pointer table. Needs a C
# compiler (cc); no oracle.
cshim:
	$(JERBUILD) exec --libdirs "$(LIBDIRS)" tools/cshim-test.ss

# Performance benchmark vs upstream SQLite, using SQLite's own 16-step
# "Database Speed Comparison" workload (docs/benchmarks.md). N is the
# big-transaction insert count (default 1000); `make bench N=2000` for more.
bench:
	@if [ ! -d "$(REF_DIR)" ]; then echo "SKIP bench: reference $(REF_DIR) not found"; exit 0; fi; \
	$(MAKE) --no-print-directory -C $(REF_DIR) build >/dev/null || \
	  { echo "SKIP bench: reference build failed"; exit 0; }; \
	DYLD_LIBRARY_PATH=$(REF_DIR) LD_LIBRARY_PATH=$(REF_DIR) \
	$(JERBUILD) exec --libdirs "$(DIFF_LIBDIRS)" tools/bench.ss $(N)

robustness: corrupt dmlfuzz concurrency fuzz

clean:
	rm -rf lib
	find . \( -name '*.wpo' -o -name '*.so' \) -delete 2>/dev/null || true

help:
	@echo "jsqlite -- SQLite3-compatible engine in Jerboa Scheme"
	@echo ""
	@echo "  make build    Compile-check all production modules"
	@echo "  make unit     Run unit tests (tests/unit/*.ss)"
	@echo "  make diff     Run differential tests vs the jerboa-sqlite oracle"
	@echo "  make test     Run unit + diff (default acceptance gate)"
	@echo "  make fuzz     Differential SQL fuzz vs the oracle (N=, SEED=)"
	@echo "  make corrupt  Malformed-database corpus robustness test (N=, SEED=)"
	@echo "  make dmlfuzz  Write-path / B-tree property test vs the oracle (N=, SEED=)"
	@echo "  make concurrency Multi-process write-locking stress test (N=)"
	@echo "  make cshim    C ABI shim: a C driver runs jsqlite prepared statements (needs cc)"
	@echo "  make bench    Performance benchmark vs upstream SQLite (N=)"
	@echo "  make robustness  corrupt + dmlfuzz + fuzz"
	@echo "  make clean    Remove build artifacts"
