2026-10-24 06:40:09 +02:00
CXX ?= g++
CXXSTD = -std= c++23
WARN = -Wall -Wextra
2026-10-25 07:12:42 +01:00
OPT ?= -O1
2026-10-24 06:40:09 +02:00
BUILD = build
VERSION = $( shell cat VERSION)
BIN = $( BUILD) /detz
GEN_INC = $( BUILD) /gen
VERSION_HPP = $( GEN_INC) /version.hpp
2026-10-25 07:12:42 +01:00
# moloapp (src/cli includes tenancy/strategy.h). ../../moloapp/src resolves only from the main
# checkout (taicpp is /home/retoor/projects/tai/taicpp); from a worktree under
# .claude/worktrees/<id>/taicpp it is five levels up. Override with MOLOAPP_SRC=...
MOLOAPP_SRC ?= $( firstword $( wildcard ../../moloapp/src ../../../../../moloapp/src $( HOME) /projects/moloapp/src))
ifeq ( $( MOLOAPP_SRC ) ,)
$(error moloapp/src not found : set MOLOAPP_SRC =/path /to /moloapp /src )
endif
# Include path: every src directory (headers include their own directory first, so same-named
# headers in two modules do not collide).
SRC_DIRS = $( shell find src -type d)
2026-10-27 18:22:24 +01:00
INC = -I$( GEN_INC) $( addprefix -I,$( SRC_DIRS)) -I$( MOLOAPP_SRC) -Ithird_party/ed25519 -Ithird_party/qrcodegen
2026-10-25 07:12:42 +01:00
CXXFLAGS ?= $( CXXSTD) $( WARN) $( OPT)
2026-10-27 18:22:24 +01:00
LDLIBS = -lsqlite3 -lcurl -lz -pthread -lutil -lssl -lcrypto
2026-10-25 07:12:42 +01:00
2026-10-24 06:42:23 +02:00
ALL_SRCS = $( shell find src -name '*.cpp' )
2026-10-25 07:12:42 +01:00
ALL_OBJS = $( patsubst %.cpp,$( BUILD) /obj/%.o,$( ALL_SRCS))
2026-10-27 18:22:24 +01:00
# Vendored C: the public-domain ed25519 ref10 curve arithmetic (third_party/ed25519,
# zlib license) that detz::wa WaCrypto needs for XEdDSA, the one Signal primitive OpenSSL
# does not expose. Compiled as C (its static load_3/load_4 collide if merged into one TU).
TP_C_SRCS = $( wildcard third_party/ed25519/*.c)
TP_C_OBJS = $( patsubst %.c,$( BUILD) /obj/%.o,$( TP_C_SRCS))
# Vendored C++: the nayuki QR Code generator (third_party/qrcodegen, MIT) for the
# `--whatsapp-link` terminal wizard.
TP_CPP_SRCS = $( wildcard third_party/qrcodegen/*.cpp)
TP_C_OBJS += $( patsubst %.cpp,$( BUILD) /obj/%.o,$( TP_CPP_SRCS))
2026-10-25 07:12:42 +01:00
MAIN_OBJ = $( BUILD) /obj/main.o
LIB = $( BUILD) /libdetz.a
.PHONY : all static lint test test -build test -run parity parity -check version -bump changelog tag release release -dry docs clean
2026-10-24 06:40:09 +02:00
all : $( BIN )
2026-10-25 07:12:42 +01:00
$(VERSION_HPP) : VERSION src /cli /version .hpp .in
2026-10-24 06:40:09 +02:00
@mkdir -p $( GEN_INC)
@sed 's/@VERSION@/$(VERSION)/' src/cli/version.hpp.in > $@
2026-10-25 07:12:42 +01:00
$(BUILD)/obj/%.o : %.cpp $( VERSION_HPP )
2026-10-24 06:42:23 +02:00
@mkdir -p $( dir $@ )
2026-10-25 07:12:42 +01:00
$( CXX) $( CXXFLAGS) $( INC) -MMD -MP -c $< -o $@
2026-10-24 06:42:23 +02:00
2026-10-27 18:22:24 +01:00
$(BUILD)/obj/%.o : %.c
@mkdir -p $( dir $@ )
$( CC) -O2 -Wall -Ithird_party/ed25519 -c $< -o $@
$(LIB) : $( ALL_OBJS ) $( TP_C_OBJS )
2026-10-25 07:12:42 +01:00
@rm -f $@
ar rcs $@ $^
2026-10-24 06:40:09 +02:00
2026-10-27 18:22:24 +01:00
$(BIN) : $( MAIN_OBJ ) $( ALL_OBJS ) $( TP_C_OBJS )
$( CXX) $( CXXFLAGS) $( MAIN_OBJ) $( ALL_OBJS) $( TP_C_OBJS) $( LDLIBS) -o $@
2026-10-24 06:40:09 +02:00
2026-10-27 18:22:24 +01:00
static : $( MAIN_OBJ ) $( ALL_OBJS ) $( TP_C_OBJS )
$( CXX) $( CXXFLAGS) -pthread -static $( MAIN_OBJ) $( ALL_OBJS) $( TP_C_OBJS) third_party/curl/libcurl.a third_party/sqlite/libsqlite3.a -lssl -lcrypto third_party/jitterentropy/libjitterentropy.a -lzstd -lz -lnghttp2 -lpsl -lbrotlidec -lidn2 -lrtmp -o $( BUILD) /detz-static
2026-10-24 06:42:23 +02:00
2026-10-25 07:12:42 +01:00
# ---------------------------------------------------------------------------
# Test binaries. Every test links tests/<name>.cpp against build/libdetz.a (the archive pulls
# only the objects a test needs, main.cpp is never in it). No test links main.cpp.
# TESTBIN(output, source, optional include flags). Several modules ship same-named headers
# (text.h, scheduler.h, consts.h, ...), so a test whose flat includes mean one module gets only
# that module's -I dirs.
MISC_INC = -Isrc/skills -Isrc/sched -Isrc/telegram -Isrc/rag -Isrc/install
SHELL_INC = -Isrc/util -Isrc/shell
define TESTBIN
TEST_BINS += $( 1)
LINT_TESTS += $( 2)
LINT_INC_$(2) := $(if $( 3) ,$( 3) ,$$ ( INC) )
$(1) : $( 2) $( LIB ) $( VERSION_HPP )
@mkdir -p $$ ( dir $$ @)
$$ ( CXX) $$ ( CXXFLAGS) $(if $( 3) ,$( 3) ,$$ ( INC) ) $( 2) $$ ( LIB) $$ ( LDLIBS) -o $$ @
endef
2026-10-24 06:40:09 +02:00
2026-10-25 07:12:42 +01:00
TEST_BINS =
LINT_TESTS =
# foundation (store, config, session, plexer, repl)
$( eval $( call TESTBIN ,$( BUILD ) /foundation_test ,tests /foundation_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /foundation_probe ,tests /foundation_probe .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /repl_probe ,tests /repl_probe .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /store_test ,tests /store_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /cli_test ,tests /cli_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /util_config_test ,tests /util_config_test .cpp ))
# agent core
$( eval $( call TESTBIN ,$( BUILD ) /agent_diff ,tests /agent_diff .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /agent_e 2e ,tests /agent_e 2e .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /agent_sched ,tests /agent_sched .cpp ))
# shell and util (shell_parity_driver is built by tests/shell_parity.py itself; lint only)
LINT_TESTS += tests/shell_parity_driver.cpp
LINT_INC_tests/shell_parity_driver.cpp : = $( SHELL_INC )
$( eval $( call TESTBIN ,$( BUILD ) /shell_test ,tests /shell_test .cpp ,$( SHELL_INC )))
# cap and mcp
$( eval $( call TESTBIN ,$( BUILD ) /capmcp /capmcp_unit ,tests /capmcp_unit .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /capmcp /capmcp_probe ,tests /capmcp_probe .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /capmcp /capmcp_cli ,tests /capmcp_cli .cpp ))
2026-10-25 18:01:34 +01:00
$( eval $( call TESTBIN ,$( BUILD ) /capmcp /cap_model_catalog_test ,tests /cap_model_catalog_test .cpp ))
2026-10-25 18:13:20 +01:00
$( eval $( call TESTBIN ,$( BUILD ) /fuzzy_test ,tests /fuzzy_test .cpp ))
2026-10-27 18:22:24 +01:00
$( eval $( call TESTBIN ,$( BUILD ) /whatsapp_test ,tests /whatsapp_test .cpp ,-Isrc /whatsapp -Isrc /llm -Ithird_party /ed 25519))
$( eval $( call TESTBIN ,$( BUILD ) /termshot_test ,tests /termshot_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /muse_flags_test ,tests /muse_flags_test .cpp ))
2026-10-27 21:32:38 +01:00
$( eval $( call TESTBIN ,$( BUILD ) /llm_history_test ,tests /llm_history_test .cpp ))
2026-10-25 18:58:22 +01:00
$( eval $( call TESTBIN ,$( BUILD ) /orch_test ,tests /orch_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /orch_live ,tests /orch_live .cpp ))
2026-10-25 07:12:42 +01:00
# misc (skills, sched, telegram, rag, install)
$( eval $( call TESTBIN ,$( BUILD ) /misc /misc_test ,tests /misc_test .cpp ,$( MISC_INC )))
$( eval $( call TESTBIN ,$( BUILD ) /misc /misc_driver ,tests /misc_driver .cpp ,$( MISC_INC )))
# tui and grr
$( eval $( call TESTBIN ,$( BUILD ) /tui_spec ,tests /tui_spec .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /tui_shot ,tests /tui_shot .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /tui_script ,tests /tui_script .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /tui_rows ,tests /tui_rows .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /tui_chat ,tests /tui_chat .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /tui_grr ,tests /tui_grr .cpp ))
# mplex
$( eval $( call TESTBIN ,$( BUILD ) /mplex_cli ,tests /mplex_cli_main .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_foundation_test ,tests /mplex_foundation_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_core_test ,tests /mplex_core_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_model_test ,tests /mplex_model_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_copy_test ,tests /mplex_copy_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_vt_test ,tests /mplex_vt_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_flow_test ,tests /mplex_flow_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_regress_test ,tests /mplex_regress_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_spec_test ,tests /mplex_spec_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_server_test ,tests /mplex_server_test .cpp ))
$( eval $( call TESTBIN ,$( BUILD ) /mplex_commands_test ,tests /mplex_commands_test .cpp ))
# test-build compiles every test binary; test-run executes them (tests/run_all.py) and prints the
# summary table; `make test` does both and fails if any step fails or any binary does not build.
test-build : $( TEST_BINS )
test-run :
python3 tests/run_all.py
test : test -build
python3 tests/run_all.py
# lint: real -Werror -fsyntax-only over every source, main.cpp and every test source, in parallel.
# Test sources use the same include set their binary is built with (see TESTBIN).
LINT_SRCS = $( ALL_SRCS) main.cpp
lint : $( VERSION_HPP )
@mkdir -p $( BUILD)
@: > $( BUILD) /lint.cmds
@$( foreach f,$( LINT_SRCS) ,echo '$(CXX) $(CXXSTD) $(WARN) -Werror -fsyntax-only $(INC) $(f) || { echo "LINT FAIL $(f)"; exit 1; }' >> $( BUILD) /lint.cmds; )
@$( foreach f,$( LINT_TESTS) ,echo '$(CXX) $(CXXSTD) $(WARN) -Werror -fsyntax-only $(LINT_INC_$(f)) $(f) || { echo "LINT FAIL $(f)"; exit 1; }' >> $( BUILD) /lint.cmds; )
@if xargs -d '\n' -P $$ ( nproc) -I{} sh -c {} < $( BUILD) /lint.cmds; then echo "lint: $$ (wc -l < $( BUILD) /lint.cmds) files clean" ; else echo "lint: FAILED" ; exit 1; fi
# Parity verification (docs/wiring/verify.md). Output goes to build/parity/ and PARITY.md.
parity : $( BIN )
DETZ_BIN = $( abspath $( BIN)) tools/parity.sh run --detz $( abspath $( BIN))
parity-check : $( BIN )
DETZ_BIN = $( abspath $( BIN)) tools/parity.sh check --detz $( abspath $( BIN))
2026-10-24 06:40:09 +02:00
version-bump :
tools/version-bump.sh $( PART)
changelog :
tools/changelog.sh " $( MSG) "
tag :
tools/tag.sh
release :
PART = $( PART) MSG = " $( MSG) " tools/release.sh
2026-10-24 06:42:23 +02:00
release-dry :
PART = $( PART) MSG = " $( MSG) " sh -n tools/release.sh && echo "release dry-run OK"
docs :
2026-10-25 19:16:18 +01:00
@for f in agent cap cap-agents fuzzy orchestration mcp tui mplex shell store util llm misc foundation parity; do test -f docs/$$ f.md || { echo "missing docs/ $$ f.md" ; exit 1; } ; done ; echo "docs OK"
2026-10-24 06:42:23 +02:00
2026-10-24 06:40:09 +02:00
clean :
rm -rf $( BUILD)
2026-10-25 07:12:42 +01:00
-include $( shell find $( BUILD ) /obj -name '*.d ' 2>/dev /null )