CXX       = g++
CXXFLAGS  = -std=c++17 -Wall -Wextra -Werror -O2 -fPIC
INCLUDE   = -Iinclude
SYSLIBS   = -lbluetooth -lsystemd -lpthread -lrt
LIBS      = -lantheos -lmembus -lsockbus -lblebus $(SYSLIBS)
CRYPTO    = -lssl -lcrypto

SRC       = src/antpeer.cpp
CSRC      = src/Ed25519Utils.cpp
OBJ       = build/antpeer.o
COBJ      = build/Ed25519Utils.o
LIB       = build/libantpeer.a
SO        = build/libantpeer.so
TEST      = build/unit_test
CTEST     = build/test_ed25519

PREFIX    = /usr/local

# The monorepo/standalone selector, used by BOTH the header block below and
# the family-library link block further down. Defined here because a variable
# used in a PREREQUISITE is expanded when the rule is READ: the include block
# sat below the object rules and expanded to nothing there, so the staging dir
# was not built before the compile that needed it.
FAMILY_SRC := $(wildcard ../antheos/include/antheos.hpp)

# ── Family HEADERS, for the COMPILE ──
#
# ANTPEER-TESTS-COMPILE-AGAINST-INSTALLED-HEADERS — the selector above pinned
# the LINK to the sibling trees and left the compile pointing at /usr/local.
# `INCLUDE = -Iinclude` covers antpeer's own headers only, so all four family
# includes fell through to the system path: measured with `g++ -H`, every one of
# <antheos/antheos.hpp>, <membus/membus.hpp>, <sockbus.hpp> and <blebus.hpp>
# resolved to /usr/local/include.
#
# That was not latent. Three of the four installed headers had already drifted
# from the tree beside them — the tree's antheos.hpp declares a public
# `flags_conform(WordType, Radix, Unit)` that the installed copy does not have.
# So the library and both suites compiled against June declarations while
# linking August archives, and reported green.
#
# The spellings differ across the family — <antheos/antheos.hpp> is a subdir
# spelling over a FLAT in-tree include/ — and you cannot -I your way from one to
# the other. A staging directory of symlinks supplies the missing level, so no
# source line changes and the published header spelling a third-party consumer
# writes is untouched. It is applied to every compile in this tree rather than
# only the two files that need it today: a family include added later to a file
# not on that list would resolve to /usr/local silently, which is this defect
# recurring.
#
# Same selector as the link half, for the same reason — a standalone checkout
# has no siblings and MUST use the installed headers.

ifeq ($(FAMILY_SRC),)
  FAMILY_INC =
  FAMILY_INC_DEP =
else
  FAMILY_INC     = -Ibuild/famin -I../sockbus/include -I../blebus/include
  FAMILY_INC_DEP = build/famin/.stamp
endif

# MAKE'S DEFAULT GOAL IS THE FIRST TARGET IN THE FILE, not the one named
# `all`. The staging rule below has to sit above the rules that name
# $(FAMILY_INC_DEP) as a PREREQUISITE — a variable in a prerequisite expands
# when the rule is READ, so a definition below them expands to nothing while
# looking correct in a recipe — and putting it there made it the default goal.
# Bare `make` then built one symlink directory and stopped, reporting success
# for building nothing. Nothing here caught it: every gate names its target
# (`make test`, `make install`), so the only caller who types bare `make` is a
# person, or a third party building this MIT library from a tarball.
.DEFAULT_GOAL := all

build/famin/.stamp: | build
	mkdir -p build/famin
	ln -sfn ../../../antheos/include build/famin/antheos
	ln -sfn ../../../membus/include  build/famin/membus
	touch $@

.PHONY: all clean test install uninstall

all: $(LIB) $(SO)

build:
	mkdir -p build

build/antpeer.o: src/antpeer.cpp include/antpeer.hpp $(FAMILY_INC_DEP) | build
	$(CXX) $(CXXFLAGS) $(INCLUDE) $(FAMILY_INC) -c $< -o $@

build/Ed25519Utils.o: src/Ed25519Utils.cpp include/Ed25519Utils.hpp $(FAMILY_INC_DEP) | build
	$(CXX) $(CXXFLAGS) $(INCLUDE) $(FAMILY_INC) -c $< -o $@

$(LIB): $(OBJ) $(COBJ)
	ar rcs $@ $^

# The SHARED library records DT_NEEDED on shared siblings — `-l` names, not
# paths — because that is what an installed consumer resolves against at run
# time. What changed 2026-08-25 is only WHERE the linker finds them while
# building: `-L` on the sibling build directories rather than falling through to
# /usr/local/lib.
#
# Before, this link resolved -lantheos against an installed copy dated
# 2026-06-05, so a `.so` built in this tree recorded a dependency on a library
# eleven weeks behind the sources beside it, and said nothing
# (ANTHEOS-STALE-INSTALLED-LIBRARY-AT-USR-LOCAL). When that copy was removed
# the link failed outright — which is the removal working, and is also how this
# was found: `make test` stayed green throughout, because the test path links
# the sibling ARCHIVES and never builds this target at all.
#
# Same FAMILY_SRC selector as the tests, for the same reason: a standalone
# checkout has no siblings and must resolve installed copies, a monorepo
# checkout has them and must not prefer an older installed one. `-L` does not
# reach DT_NEEDED, so the soname recorded is identical either way.
$(SO): $(OBJ) $(COBJ) $(FAMILY_SO_DEPS)
	$(CXX) -shared -o $@ $^ $(FAMILY_SO_L) $(LIBS) $(CRYPTO)


# ── Family libraries, for the TEST link ──
#
# ANTPEER-TESTS-LINK-THE-INSTALLED-ANTHEOS — `-lantheos -lmembus -lsockbus
# -lblebus` carried no -L and nothing pinned them at run time, so every
# `make test` graded /usr/local/lib copies dated 2026-06-05 rather than the
# libraries this tree sits beside. libantpeer itself was pinned only by an
# `LD_LIBRARY_PATH=build` on the run line, which does not travel with the binary.
#
# Two situations are genuinely different and both have to work:
#
#   standalone checkout — the sibling trees do not exist. The INSTALLED
#                         libraries are the dependency, and naming them with -l
#                         is correct for a third-party consumer.
#   LangSyn monorepo    — the siblings are present. `make test` must grade THEM;
#                         an installed copy can be months older than the tree.
#
# The selector is the presence of a sibling SOURCE tree. That is a property of
# the checkout and does not change with what has been built — a build-order
# selector would reintroduce MEMBUS-TEST-LINK-IS-BUILD-ORDER-DEPENDENT here.
# In the monorepo the sibling archives are prerequisites and are named on the
# link line, so the dependency and the link are the same file: the base/antheos
# rule, applied one layer up.
#
# $(SO) is unchanged: a shared library should record DT_NEEDED on shared
# siblings, which is what an installed consumer resolves against.

FAMILY      = antheos membus sockbus blebus

ifeq ($(FAMILY_SRC),)
  TEST_FAMILY = -lantheos -lmembus -lsockbus -lblebus
  FAMILY_DEPS =
  # Standalone checkout: the installed copies ARE the dependency, and the
  # default search path is where they live. Nothing to add.
  FAMILY_SO_L    =
  FAMILY_SO_DEPS =
else
  TEST_FAMILY = $(foreach l,$(FAMILY),../$(l)/build/lib$(l).a)
  FAMILY_DEPS = $(TEST_FAMILY)
  FAMILY_SO_L    = $(foreach l,$(FAMILY),-L../$(l)/build)
  FAMILY_SO_DEPS = $(foreach l,$(FAMILY),../$(l)/build/lib$(l).so)
endif

../antheos/build/libantheos.so: ; $(MAKE) -C ../antheos build/libantheos.so
../membus/build/libmembus.so:   ; $(MAKE) -C ../membus  build/libmembus.so
../sockbus/build/libsockbus.so: ; $(MAKE) -C ../sockbus build/libsockbus.so
../blebus/build/libblebus.so:   ; $(MAKE) -C ../blebus  build/libblebus.so

../antheos/build/libantheos.a: ; $(MAKE) -C ../antheos build/libantheos.a
../membus/build/libmembus.a:   ; $(MAKE) -C ../membus  build/libmembus.a
../sockbus/build/libsockbus.a: ; $(MAKE) -C ../sockbus build/libsockbus.a
../blebus/build/libblebus.a:   ; $(MAKE) -C ../blebus  build/libblebus.a

$(TEST): tests/unit_test.cpp $(LIB) $(FAMILY_DEPS) $(FAMILY_INC_DEP) | build
	$(CXX) $(CXXFLAGS) $(INCLUDE) $(FAMILY_INC) tests/unit_test.cpp -o $@ $(LIB) $(TEST_FAMILY) $(SYSLIBS) $(CRYPTO)

$(CTEST): tests/test_ed25519.cpp $(LIB) $(FAMILY_DEPS) $(FAMILY_INC_DEP) | build
	$(CXX) $(CXXFLAGS) $(INCLUDE) $(FAMILY_INC) tests/test_ed25519.cpp -o $@ $(LIB) $(TEST_FAMILY) $(SYSLIBS) $(CRYPTO)

test: $(TEST) $(CTEST)
	./scripts/header-origin-check.sh
	./$(TEST)
	./$(CTEST)

# Headers install into $(PREFIX)/include/antpeer/, matching the two family
# members that already do it (`antheos/`, `membus/`) — ANTPEER-HEADERS-INSTALL-
# FLAT. A flat install puts a header in a namespace it does not own: this
# library's `Ed25519Utils.hpp` declares `antpeer::crypto::KeyPair`, and
# `pki-reg/Ed25519Utils.hpp` in this same repo declares `pkireg::KeyPair` under
# the identical filename. Nothing has collided, because pki-reg includes its own
# with quotes and the quoted form searches the including file's directory first.
# That is the only thing standing between the two, and it is a property of how
# the consumer writes its include rather than of where we put ours.
#
# CLEAN BREAK, no compatibility shim at the flat path — decided by measurement,
# not by preference. The flat headers have ZERO consumers: `chrysalis` says so
# in its own CMakeLists ("no antpeer/sockbus/blebus"), another consumer in
# this repository was retired 2026-06-02 with its binary and units removed,
# this project builds against `include/` directly, and a full scan of 769
# executables under /usr/local/bin, /opt and /srv on the build host found
# nothing linking libantpeer at all. A shim for no consumer is a second copy to keep in step.
install: $(LIB) $(SO)
	install -d $(PREFIX)/lib $(PREFIX)/include/antpeer
	install -m 644 $(LIB) $(PREFIX)/lib/
	install -m 755 $(SO) $(PREFIX)/lib/
	install -m 644 include/antpeer.hpp $(PREFIX)/include/antpeer/
	install -m 644 include/Ed25519Utils.hpp $(PREFIX)/include/antpeer/
	ldconfig

# Removes the DIRECTORY, and the flat copies a pre-2026-08-25 install left
# behind. `rmdir` rather than `rm -rf`: the directory is ours to remove only if
# it is empty after our two files go, and something else having put a file there
# is a fact the operator should meet as a non-empty rmdir rather than as a
# silent deletion.
uninstall:
	rm -f $(PREFIX)/lib/libantpeer.so $(PREFIX)/lib/libantpeer.a
	rm -f $(PREFIX)/include/antpeer/antpeer.hpp
	rm -f $(PREFIX)/include/antpeer/Ed25519Utils.hpp
	-rmdir $(PREFIX)/include/antpeer
	rm -f $(PREFIX)/include/antpeer.hpp
	rm -f $(PREFIX)/include/Ed25519Utils.hpp
	ldconfig

clean:
	rm -rf build
