summaryrefslogtreecommitdiffstats
path: root/Makefile
blob: c7ff2baf225669365fa0e770253a3e597410fdf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
VERSION    := 0.1.0
SOVERSION  := 0

CC         ?= cc
AR         ?= ar
CLANG_TIDY ?= clang-tidy
FORMAT     ?= clang-format
PREFIX     ?= /usr/local
LIBDIR     ?= $(PREFIX)/lib
INCLUDEDIR ?= $(PREFIX)/include
BINDIR     ?= $(PREFIX)/bin

FEATURES  := -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE
WARNINGS  := -Wall -Wextra -Wpedantic -Wshadow -Wstrict-prototypes \
             -Wmissing-prototypes -Wold-style-definition -Wvla \
             -Wpointer-arith -Wcast-qual -Wredundant-decls

SAN       := -fsanitize=address,undefined -fno-omit-frame-pointer \
             -fno-sanitize-recover=all

CPPFLAGS  += -Iinclude -Isrc $(FEATURES)
CFLAGS    ?= -O2 -g
CFLAGS    += -std=c11 $(WARNINGS) $(EXTRA_CFLAGS)
LDFLAGS   += $(EXTRA_LDFLAGS)
LDLIBS    += -lcurl -lm

BUILD     := build
LIB_SRC   := src/client.c src/model.c src/util.c src/json.c src/csv.c
LIB_OBJ   := $(LIB_SRC:%.c=$(BUILD)/%.o)
PIC_OBJ   := $(LIB_SRC:%.c=$(BUILD)/%.pic.o)
TEST_SRC  := tests/test_cepis.c tests/test_server.c
TEST_OBJ  := $(TEST_SRC:%.c=$(BUILD)/%.o)
ALL_SRC   := $(LIB_SRC) src/cli.c $(TEST_SRC)
ALL_HDR   := include/cepis.h src/internal.h src/json.h src/csv.h \
             tests/test_server.h

STATIC    := $(BUILD)/libcepis.a
SHARED    := $(BUILD)/libcepis.so.$(VERSION)
CLI       := $(BUILD)/cepis
TESTS     := $(BUILD)/test_cepis

.DEFAULT_GOAL := help
.PHONY: help build test lint format check clean memcheck sanitize install \
	uninstall

help:
	@echo 'Targets:'
	@echo '  build      static and shared libraries, plus the cepis CLI'
	@echo '  test       build and run the suite against a local HTTP server'
	@echo '  lint       clang-format --dry-run + clang-tidy (read-only)'
	@echo '  format     apply clang-format (writes)'
	@echo '  check      lint, then test, then build'
	@echo '  memcheck   run the suite under valgrind'
	@echo '  sanitize   rebuild and run the suite under ASan + UBSan'
	@echo '  install    install headers, libraries and the CLI under PREFIX'
	@echo '  clean      remove $(BUILD)/'
	@echo
	@echo 'Override the compiler with e.g. make test CC=clang'

build: $(STATIC) $(SHARED) $(CLI)

$(BUILD)/%.o: %.c
	@mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

$(BUILD)/%.pic.o: %.c
	@mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c $< -o $@

$(STATIC): $(LIB_OBJ)
	@mkdir -p $(dir $@)
	$(AR) rcs $@ $(LIB_OBJ)

$(SHARED): $(PIC_OBJ)
	@mkdir -p $(dir $@)
	$(CC) -shared -Wl,-soname,libcepis.so.$(SOVERSION) $(PIC_OBJ) \
		$(LDFLAGS) $(LDLIBS) -o $@
	ln -sf libcepis.so.$(VERSION) $(BUILD)/libcepis.so.$(SOVERSION)
	ln -sf libcepis.so.$(VERSION) $(BUILD)/libcepis.so

$(CLI): $(BUILD)/src/cli.o $(STATIC)
	$(CC) $(BUILD)/src/cli.o $(STATIC) $(LDFLAGS) $(LDLIBS) -o $@

$(TESTS): $(TEST_OBJ) $(STATIC)
	$(CC) $(TEST_OBJ) $(STATIC) $(LDFLAGS) $(LDLIBS) -lpthread -o $@

test: $(TESTS)
	$(TESTS)

# Read-only by design, so it is safe in CI and in a pre-commit hook: it reports
# what `make format` would change without changing anything.
lint:
	$(FORMAT) --dry-run --Werror $(ALL_SRC) $(ALL_HDR)
	$(CLANG_TIDY) --quiet $(ALL_SRC) -- $(CPPFLAGS) -std=c11

format:
	$(FORMAT) -i $(ALL_SRC) $(ALL_HDR)

# The order matters: lint is the fastest to fail, build the slowest, so a
# broken run reports the cheapest problem first.
check: lint test build

memcheck: $(TESTS)
	valgrind --error-exitcode=9 --leak-check=full --show-leak-kinds=definite,indirect \
		--errors-for-leak-kinds=definite,indirect --track-origins=yes $(TESTS)

# Rebuilt from scratch: the sanitizers change the ABI of the objects, so they
# must not be mixed with objects from an ordinary build.
sanitize:
	$(MAKE) clean
	$(MAKE) test EXTRA_CFLAGS='$(SAN)' EXTRA_LDFLAGS='$(SAN)'
	$(MAKE) clean

install: build
	install -d $(DESTDIR)$(INCLUDEDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(BINDIR)
	install -m 644 include/cepis.h $(DESTDIR)$(INCLUDEDIR)/
	install -m 644 $(STATIC) $(DESTDIR)$(LIBDIR)/
	install -m 755 $(SHARED) $(DESTDIR)$(LIBDIR)/
	ln -sf libcepis.so.$(VERSION) $(DESTDIR)$(LIBDIR)/libcepis.so.$(SOVERSION)
	ln -sf libcepis.so.$(VERSION) $(DESTDIR)$(LIBDIR)/libcepis.so
	install -m 755 $(CLI) $(DESTDIR)$(BINDIR)/

uninstall:
	rm -f $(DESTDIR)$(INCLUDEDIR)/cepis.h
	rm -f $(DESTDIR)$(LIBDIR)/libcepis.a
	rm -f $(DESTDIR)$(LIBDIR)/libcepis.so*
	rm -f $(DESTDIR)$(BINDIR)/cepis

clean:
	rm -rf $(BUILD)