# NOTE: only tested in GNU/Linux. SHELL := /bin/bash ## Binary dependencies. CC = gcc AR = ar # Global switches. # Compile with debug symbols. Libraries are suffixed with _dbg and can coexist # with non-debug libraries. DEBUG ?= 0 # Install in a user-owned directory provided as LOCAL_PREFIX or a default # (~/.local), instead of a system directory. LOCAL ?= 0 ## Paths. LOCAL_PREFIX ?= $(HOME)/.local ifneq ($(LOCAL), 0) PREFIX ?= $(LOCAL_PREFIX) else PREFIX ?= /usr/local endif bindir := $(PREFIX)/bin libdir := $(PREFIX)/lib INCLUDEDIR := $(PREFIX)/include BUILDDIR := ./build OUTDIR := ./bin TMPDIR ?= /tmp VALGRIND_DUMP := $(TMPDIR)/lsup_valgrind.log CALLGRIND_DUMP := $(TMPDIR)/lsup_callgrind.out MASSIF_DUMP := $(TMPDIR)/lsup_massif.out INCLUDE_BASE := . -Iinclude -Iext/tpl/src -Iext/hashmap -Iext/log/src INCLUDE := -I$(INCLUDE_BASE) _CFLAGS = -std=gnu11 -Wall -fPIC $(INCLUDE) ifneq ($(DEBUG), 0) CFLAGS = $(_CFLAGS) -Itest -O0 -ggdb -DDEBUG else CFLAGS = $(_CFLAGS) -O3 endif $(info CFLAGS: $(CFLAGS)) # NOTE: -luuid is a Linux system library. Other OS's might need a different # link or a non-system library built. LDFLAGS := -L$(OUTDIR) -L$(libdir) -L. -llmdb -lxxhash -luuid PARSER = bin/lemon LEMON_SRC = ext/sqlite/tool/lemon.c CODEC_DIR = src/codec # External sources compiled in core object. EXT_SRC := $(wildcard ext/log/src/*.c) \ $(wildcard ext/hashmap/*.c) \ $(wildcard ext/tpl/src/*.c) # External headers of libraries compiled in core. EXT_H := $(wildcard ext/log/src/*.h) \ $(wildcard ext/tpl/src/*.h) \ $(wildcard ext/hashmap/*.h) LSUP_SRC = $(wildcard src/*.c) SRC = $(EXT_SRC) $(LSUP_SRC) TEST_SRC = $(wildcard test/*.c) test.c EXT_OBJ := $(EXT_SRC:%.c=$(BUILDDIR)/%.o) # TODO This is extremely convoluted, simplify if possible. CODEC_SRC := $(wildcard $(CODEC_DIR)/codec_*.c) CODEC_REL_SRC := $(CODEC_SRC:$(CODEC_DIR)/%=%) ALL_CODEC_REL_SRC := $(CODEC_REL_SRC) $(CODEC_REL_SRC:codec_%=parser_%) \ $(CODEC_REL_SRC:codec_%=grammar_%) CODEC_SRC = $(ALL_CODEC_REL_SRC:%=$(CODEC_DIR)/%) ifneq ($(DEBUG), 0) CODEC_OBJ = $(patsubst $(CODEC_DIR)/%.c, $(BUILDDIR)/%_dbg.o, $(CODEC_SRC)) else CODEC_OBJ = $(patsubst $(CODEC_DIR)/%.c, $(BUILDDIR)/%.o, $(CODEC_SRC)) endif ifneq ($(DEBUG), 0) STATIC_LIB = $(OUTDIR)/liblsuprdf_dbg.a LOCAL_OBJ = $(LSUP_SRC:src/%.c=$(BUILDDIR)/%_dbg.o) else STATIC_LIB = $(OUTDIR)/liblsuprdf.a LOCAL_OBJ = $(LSUP_SRC:src/%.c=$(BUILDDIR)/%.o) endif OBJ = $(EXT_OBJ) $(LOCAL_OBJ) DYN_LIB = $(STATIC_LIB:.a=.so) LIBS = $(STATIC_LIB) $(DYN_LIB) $(info EXT_SRC: $(EXT_SRC)) $(info EXT_OBJ: $(EXT_OBJ)) $(info OBJ: $(OBJ)) $(info LIBS: $(LIBS)) # LDD for Linux, otool -L for OSX. ifeq (, $(shell which ldd)) LDD := otool -L else LDD := ldd endif # For visual dep graph. DEPS := $(shell echo "${INCLUDE_BASE}" | sed -e 's/ -I/,/g'),include/codec DOCS = docs ## Environment. # Tests need the freshly compiled libs. export LD_LIBRARY_PATH = $(OUTDIR):$(libdir) ## Rules. .DEFAULT_GOAL := lib # Extract all rule comments into a help message. .PHONY: help help: @echo "Command overview:"; echo; \ grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1|\3/p' \ | column -t -s '|' .PHONY: lib lib: codec $(LIBS) ## Compile main library (static and dynamic linking). # Static library. $(STATIC_LIB): $(OBJ) $(AR) rs $@ $^ $(CODEC_OBJ) # Dynamic library. $(DYN_LIB): $(OBJ) $(CC) -shared $(LDFLAGS) -o $@ $^ $(CODEC_OBJ) # External libraries. $(BUILDDIR)/ext/%.o: ext/%.c mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $^ -o $@ # Standard objects. $(BUILDDIR)/%.o: src/%.c $(CC) $(CFLAGS) -c $^ -o $@ # Debug objects. $(BUILDDIR)/%_dbg.o: src/%.c $(CC) $(CFLAGS) -c $^ -o $@ # Codecs in a subfolder. .PHONY: codec codec: $(PARSER) mkdir -p $(BUILDDIR) && \ $(MAKE) -C $(CODEC_DIR) codec DEBUG=$(DEBUG) # Build the parser executable. $(PARSER): $(LEMON_SRC) $(CC) $^ -o $@ install: lib ## Install library and dependencies to $PREFIX. May require sudo. @echo "Installing library files in $(PREFIX)." mkdir -p $(libdir) mkdir -p $(INCLUDEDIR)/lsup cp $(LIBS) $(libdir) && \ cp -r include/lsup/* $(EXT_H) $(INCLUDEDIR)/lsup && \ cp include/*.h $(INCLUDEDIR) .PHONY: clean clean: DEBUG = 0 clean: ## Clean up artifacts, including language parsers. rm -rf $(BUILDDIR) rm -f bin/* rm -f include/codec/grammar_*.h rm -f src/codec/grammar_*.c src/codec/parser_*.c .PHONY: uninstall ## Uninstall library (not the dependencies). uninstall: rm -f $(libdir)/liblsuprdf* rm -rf $(INCLUDEDIR)/lsup* rm -f bin/test* # For testing, use debug symbols. bin/test: DEBUG = 1 bin/test: lib $(TEST_SRC) $(CC) $(DBG_CFLAGS) $(LDFLAGS) -llsuprdf_dbg \ test.c -o bin/test .PHONY: test test: bin/test ## Run a test suite. @echo "Using libraries: "; $(LDD) bin/test exec bin/test .PHONY: gdb_test gdb_test: bin/test ## Run a test suite within gdb. @echo "Using libraries: "; $(LDD) bin/test exec gdb bin/test lint: splint \ $(INCLUDE) -Itest \ -DUINT_MAX=0xFFFFFFFFUL \ -nullpass \ -posix-lib \ test.c .PHONY: memcheck memcheck: valgrind \ --leak-check=full --show-leak-kinds=all --track-origins=yes \ --log-file=$(VALGRIND_DUMP) \ ./bin/test || true @echo "Memcheck complete. Valgrind log is at $(VALGRIND_DUMP)" memtest: bin/test memcheck ## Run a test suite using Valgrind. Output to separate file. # Profiling application. bin/profile: DEBUG = 1 bin/profile: lib profile.c $(CC) $(CFLAGS) -g -DTESTING $(LDFLAGS) -llsuprdf_dbg \ profile.c -o bin/profile # Performance test application. Essentially the profiling code without debug. bin/perftest: DEBUG = 0 bin/perftest: lib profile.c $(CC) $(CFLAGS) -g $(LDFLAGS) -llsuprdf profile.c -o bin/perftest .PHONY: perftest perftest: bin/perftest ## Run a performance test by creating, inserting and looking up triples. bin/perftest .PHONY: profile profile: bin/profile ## Run a profiling session on a limited set of high-volume commands. LSUP_MDB_MAPSIZE=800000 valgrind --tool=callgrind \ --callgrind-out-file="$(CALLGRIND_DUMP)" bin/profile 1000 @echo "Profile dump written at $(CALLGRIND_DUMP) . Open it with "\ "qcachegrind, kcachegrind, etc." .PHONY: test_profile test_profile: bin/test ## Run profiling on the standard test suite. LSUP_MDB_MAPSIZE=800000 valgrind --tool=callgrind \ --callgrind-out-file="$(CALLGRIND_DUMP)" bin/test @echo "Profile dump written at $(CALLGRIND_DUMP) . Open it with "\ "qcachegrind, kcachegrind, etc." .PHONY: footprint footprint: bin/perftest ## Measure memory footprint by generating and storing 100K triples. LSUP_MDB_MAPSIZE=80000000 valgrind --tool=massif \ --massif-out-file=$(MASSIF_DUMP) bin/perftest 100000 @echo "Memory stats file written at $(MASSIF_DUMP). Open it with "\ "massif-visualizer or similar." # Requires cinclude2dot (https://www.flourish.org/cinclude2dot) and Graphviz. depgraph: $(LSUP_SRC) include/* include/lsup/* include/lsup/codec/* ## Build a visual dependency graph of the code. cinclude2dot --merge=module --include=$(DEPS) \ --exclude='test|ext' >| $(DOCS)/dev/deps.dot dot $(DOCS)/dev/deps.dot -Tpdf >| $(DOCS)/dev/deps.pdf