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 # , , and # 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 — 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