ifdef BUILDDIR # bit of a hack, but we want to make sure BUILDDIR directory structure # is correct before any commands $(if $(findstring n,$(MAKEFLAGS)),, $(shell mkdir -p \ $(BUILDDIR)/ \ $(BUILDDIR)/bd \ $(BUILDDIR)/runners \ $(BUILDDIR)/tests \ $(BUILDDIR)/benches)) endif BUILDDIR ?= . # overridable target/src/tools/flags/etc ifneq ($(wildcard test.c main.c),) TARGET ?= $(BUILDDIR)/lfs else TARGET ?= $(BUILDDIR)/liblfs.a endif CC ?= gcc AR ?= ar SIZE ?= size CTAGS ?= ctags NM ?= nm OBJDUMP ?= objdump VALGRIND ?= valgrind GDB ?= gdb PERF ?= perf SRC ?= $(filter-out $(wildcard *.*.c),$(wildcard *.c)) OBJ := $(SRC:%.c=$(BUILDDIR)/%.o) DEP := $(SRC:%.c=$(BUILDDIR)/%.d) ASM := $(SRC:%.c=$(BUILDDIR)/%.s) CI := $(SRC:%.c=$(BUILDDIR)/%.ci) GCDA := $(SRC:%.c=$(BUILDDIR)/%.t.a.gcda) TESTS ?= $(wildcard tests/*.toml) TEST_SRC ?= $(SRC) \ $(filter-out $(wildcard bd/*.*.c),$(wildcard bd/*.c)) \ runners/test_runner.c TEST_RUNNER ?= $(BUILDDIR)/runners/test_runner TEST_TC := $(TESTS:%.toml=$(BUILDDIR)/%.t.c) \ $(TEST_SRC:%.c=$(BUILDDIR)/%.t.c) TEST_TAC := $(TEST_TC:%.t.c=%.t.a.c) TEST_OBJ := $(TEST_TAC:%.t.a.c=%.t.a.o) TEST_DEP := $(TEST_TAC:%.t.a.c=%.t.a.d) TEST_CI := $(TEST_TAC:%.t.a.c=%.t.a.ci) TEST_GCNO := $(TEST_TAC:%.t.a.c=%.t.a.gcno) TEST_GCDA := $(TEST_TAC:%.t.a.c=%.t.a.gcda) TEST_PERF := $(TEST_RUNNER:%=%.perf) TEST_TRACE := $(TEST_RUNNER:%=%.trace) BENCHES ?= $(wildcard benches/*.toml) BENCH_SRC ?= $(SRC) \ $(filter-out $(wildcard bd/*.*.c),$(wildcard bd/*.c)) \ runners/bench_runner.c BENCH_RUNNER ?= $(BUILDDIR)/runners/bench_runner BENCH_BC := $(BENCHES:%.toml=$(BUILDDIR)/%.b.c) \ $(BENCH_SRC:%.c=$(BUILDDIR)/%.b.c) BENCH_BAC := $(BENCH_BC:%.b.c=%.b.a.c) BENCH_OBJ := $(BENCH_BAC:%.b.a.c=%.b.a.o) BENCH_DEP := $(BENCH_BAC:%.b.a.c=%.b.a.d) BENCH_CI := $(BENCH_BAC:%.b.a.c=%.b.a.ci) BENCH_GCNO := $(BENCH_BAC:%.b.a.c=%.b.a.gcno) BENCH_GCDA := $(BENCH_BAC:%.b.a.c=%.b.a.gcda) BENCH_PERF := $(BENCH_RUNNER:%=%.perf) BENCH_TRACE := $(BENCH_RUNNER:%=%.trace) CFLAGS += -fcallgraph-info=su CFLAGS += -g3 CFLAGS += -I. CFLAGS += -std=c99 -Wall -Wextra -pedantic CFLAGS += -ftrack-macro-expansion=0 ifdef DEBUG CFLAGS += -O0 else CFLAGS += -Os endif ifdef TRACE CFLAGS += -DLFS_YES_TRACE endif ifdef YES_COV CFLAGS += --coverage endif ifdef YES_PERF CFLAGS += -fno-omit-frame-pointer endif ifdef YES_PERFBD CFLAGS += -fno-omit-frame-pointer endif ifdef VERBOSE CODEFLAGS += -v DATAFLAGS += -v STACKFLAGS += -v STRUCTFLAGS += -v COVFLAGS += -v PERFFLAGS += -v PERFBDFLAGS += -v endif # forward -j flag PERFFLAGS += $(filter -j%,$(MAKEFLAGS)) PERFBDFLAGS += $(filter -j%,$(MAKEFLAGS)) ifneq ($(NM),nm) CODEFLAGS += --nm-path="$(NM)" DATAFLAGS += --nm-path="$(NM)" endif ifneq ($(OBJDUMP),objdump) CODEFLAGS += --objdump-path="$(OBJDUMP)" DATAFLAGS += --objdump-path="$(OBJDUMP)" STRUCTFLAGS += --objdump-path="$(OBJDUMP)" PERFFLAGS += --objdump-path="$(OBJDUMP)" PERFBDFLAGS += --objdump-path="$(OBJDUMP)" endif ifneq ($(PERF),perf) PERFFLAGS += --perf-path="$(PERF)" endif TESTFLAGS += -b BENCHFLAGS += -b # forward -j flag TESTFLAGS += $(filter -j%,$(MAKEFLAGS)) BENCHFLAGS += $(filter -j%,$(MAKEFLAGS)) ifdef YES_PERF TESTFLAGS += -p$(TEST_PERF) BENCHFLAGS += -p$(BENCH_PERF) endif ifdef YES_PERFBD TESTFLAGS += -t$(TEST_TRACE) --trace-backtrace --trace-freq=100 endif ifndef NO_PERFBD BENCHFLAGS += -t$(BENCH_TRACE) --trace-backtrace --trace-freq=100 endif ifdef VERBOSE TESTFLAGS += -v TESTCFLAGS += -v BENCHFLAGS += -v BENCHCFLAGS += -v endif ifdef EXEC TESTFLAGS += --exec="$(EXEC)" BENCHFLAGS += --exec="$(EXEC)" endif ifneq ($(GDB),gdb) TESTFLAGS += --gdb-path="$(GDB)" BENCHFLAGS += --gdb-path="$(GDB)" endif ifneq ($(VALGRIND),valgrind) TESTFLAGS += --valgrind-path="$(VALGRIND)" BENCHFLAGS += --valgrind-path="$(VALGRIND)" endif ifneq ($(PERF),perf) TESTFLAGS += --perf-path="$(PERF)" BENCHFLAGS += --perf-path="$(PERF)" endif # commands ## Build littlefs .PHONY: all build all build: $(TARGET) ## Build assembly files .PHONY: asm asm: $(ASM) ## Find the total size .PHONY: size size: $(OBJ) $(SIZE) -t $^ ## Generate a ctags file .PHONY: tags tags: $(CTAGS) --totals --c-types=+p $(shell find -H -name '*.h') $(SRC) ## Show this help text .PHONY: help help: @$(strip awk '/^## / { \ sub(/^## /,""); \ getline rule; \ while (rule ~ /^(#|\.PHONY|ifdef|ifndef)/) getline rule; \ gsub(/:.*/, "", rule); \ printf " "" %-25s %s\n", rule, $$0 \ }' $(MAKEFILE_LIST)) ## Build the test-runner .PHONY: test-runner build-test ifndef NO_COV test-runner build-test: CFLAGS+=--coverage endif ifdef YES_PERF test-runner build-test: CFLAGS+=-fno-omit-frame-pointer endif ifdef YES_PERFBD test-runner build-test: CFLAGS+=-fno-omit-frame-pointer endif # note we remove some binary dependent files during compilation, # otherwise it's way to easy to end up with outdated results test-runner build-test: $(TEST_RUNNER) ifndef NO_COV rm -f $(TEST_GCDA) endif ifdef YES_PERF rm -f $(TEST_PERF) endif ifdef YES_PERFBD rm -f $(TEST_TRACE) endif ## Run the tests, -j enables parallel tests .PHONY: test test: test-runner ./scripts/test.py $(TEST_RUNNER) $(TESTFLAGS) ## List the tests .PHONY: test-list test-list: test-runner ./scripts/test.py $(TEST_RUNNER) $(TESTFLAGS) -l ## Build the bench-runner .PHONY: bench-runner build-bench ifdef YES_COV bench-runner build-bench: CFLAGS+=--coverage endif ifdef YES_PERF bench-runner build-bench: CFLAGS+=-fno-omit-frame-pointer endif ifndef NO_PERFBD bench-runner build-bench: CFLAGS+=-fno-omit-frame-pointer endif # note we remove some binary dependent files during compilation, # otherwise it's way to easy to end up with outdated results bench-runner build-bench: $(BENCH_RUNNER) ifdef YES_COV rm -f $(BENCH_GCDA) endif ifdef YES_PERF rm -f $(BENCH_PERF) endif ifndef NO_PERFBD rm -f $(BENCH_TRACE) endif ## Run the benchmarks, -j enables parallel benchmarks .PHONY: bench bench: bench-runner ./scripts/bench.py $(BENCH_RUNNER) $(BENCHFLAGS) ## List the benchmarks .PHONY: bench-list bench-list: bench-runner ./scripts/bench.py $(BENCH_RUNNER) $(BENCHFLAGS) -l ## Find the per-function code size .PHONY: code code: $(OBJ) ./scripts/code.py $^ -Ssize $(CODEFLAGS) ## Find the per-function data size .PHONY: data data: $(OBJ) ./scripts/data.py $^ -Ssize $(DATAFLAGS) ## Find the per-function stack usage .PHONY: stack stack: $(CI) ./scripts/stack.py $^ -Slimit -Sframe $(STACKFLAGS) ## Find the struct sizes .PHONY: struct struct: $(OBJ) ./scripts/struct_.py $^ -Ssize $(STRUCTFLAGS) ## Find the line/branch coverage after a test run .PHONY: cov cov: $(GCDA) $(strip ./scripts/cov.py \ $^ $(patsubst %,-F%,$(SRC)) \ -slines -sbranches \ $(COVFLAGS)) ## Find the perf results after bench run with YES_PERF .PHONY: perf perf: $(BENCH_PERF) $(strip ./scripts/perf.py \ $^ $(patsubst %,-F%,$(SRC)) \ -Scycles \ $(PERFFLAGS)) ## Find the perfbd results after a bench run .PHONY: perfbd perfbd: $(BENCH_TRACE) $(strip ./scripts/perfbd.py \ $(BENCH_RUNNER) $^ $(patsubst %,-F%,$(SRC)) \ -Serased -Sproged -Sreaded \ $(PERFBDFLAGS)) ## Find a summary of compile-time sizes .PHONY: summary sizes summary sizes: $(BUILDDIR)/lfs.csv $(strip ./scripts/summary.py -Y $^ \ -fcode=code_size \ -fdata=data_size \ -fstack=stack_limit \ -fstruct=struct_size \ --max=stack \ $(SUMMARYFLAGS)) # rules -include $(DEP) -include $(TEST_DEP) .SUFFIXES: .SECONDARY: $(BUILDDIR)/lfs: $(OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ $(BUILDDIR)/liblfs.a: $(OBJ) $(AR) rcs $@ $^ $(BUILDDIR)/lfs.code.csv: $(OBJ) ./scripts/code.py $^ -q $(CODEFLAGS) -o $@ $(BUILDDIR)/lfs.data.csv: $(OBJ) ./scripts/data.py $^ -q $(CODEFLAGS) -o $@ $(BUILDDIR)/lfs.stack.csv: $(CI) ./scripts/stack.py $^ -q $(CODEFLAGS) -o $@ $(BUILDDIR)/lfs.struct.csv: $(OBJ) ./scripts/struct_.py $^ -q $(CODEFLAGS) -o $@ $(BUILDDIR)/lfs.cov.csv: $(GCDA) ./scripts/cov.py $^ $(patsubst %,-F%,$(SRC)) -q $(COVFLAGS) -o $@ $(BUILDDIR)/lfs.perf.csv: $(BENCH_PERF) ./scripts/perf.py $^ $(patsubst %,-F%,$(SRC)) -q $(PERFFLAGS) -o $@ $(BUILDDIR)/lfs.perfbd.csv: $(BENCH_TRACE) $(strip ./scripts/perfbd.py \ $(BENCH_RUNNER) $^ $(patsubst %,-F%,$(SRC)) \ -q $(PERFBDFLAGS) -o $@) $(BUILDDIR)/lfs.csv: \ $(BUILDDIR)/lfs.code.csv \ $(BUILDDIR)/lfs.data.csv \ $(BUILDDIR)/lfs.stack.csv \ $(BUILDDIR)/lfs.struct.csv ./scripts/summary.py $^ -q $(SUMMARYFLAGS) -o $@ $(BUILDDIR)/runners/test_runner: $(TEST_OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ $(BUILDDIR)/runners/bench_runner: $(BENCH_OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ # our main build rule generates .o, .d, and .ci files, the latter # used for stack analysis $(BUILDDIR)/%.o $(BUILDDIR)/%.ci: %.c $(CC) -c -MMD $(CFLAGS) $< -o $(BUILDDIR)/$*.o $(BUILDDIR)/%.s: %.c $(CC) -S $(CFLAGS) $< -o $@ $(BUILDDIR)/%.a.c: %.c ./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@ $(BUILDDIR)/%.a.c: $(BUILDDIR)/%.c ./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@ $(BUILDDIR)/%.t.c: %.toml ./scripts/test.py -c $< $(TESTCFLAGS) -o $@ $(BUILDDIR)/%.t.c: %.c $(TESTS) ./scripts/test.py -c $(TESTS) -s $< $(TESTCFLAGS) -o $@ $(BUILDDIR)/%.b.c: %.toml ./scripts/bench.py -c $< $(BENCHCFLAGS) -o $@ $(BUILDDIR)/%.b.c: %.c $(BENCHES) ./scripts/bench.py -c $(BENCHES) -s $< $(BENCHCFLAGS) -o $@ ## Clean everything .PHONY: clean clean: rm -f $(BUILDDIR)/lfs rm -f $(BUILDDIR)/liblfs.a $(strip rm -f \ $(BUILDDIR)/lfs.csv \ $(BUILDDIR)/lfs.code.csv \ $(BUILDDIR)/lfs.data.csv \ $(BUILDDIR)/lfs.stack.csv \ $(BUILDDIR)/lfs.struct.csv \ $(BUILDDIR)/lfs.cov.csv \ $(BUILDDIR)/lfs.perf.csv \ $(BUILDDIR)/lfs.perfbd.csv) rm -f $(OBJ) rm -f $(DEP) rm -f $(ASM) rm -f $(CI) rm -f $(TEST_RUNNER) rm -f $(TEST_TC) rm -f $(TEST_TAC) rm -f $(TEST_OBJ) rm -f $(TEST_DEP) rm -f $(TEST_CI) rm -f $(TEST_GCNO) rm -f $(TEST_GCDA) rm -f $(TEST_PERF) rm -f $(TEST_TRACE) rm -f $(BENCH_RUNNER) rm -f $(BENCH_BC) rm -f $(BENCH_BAC) rm -f $(BENCH_OBJ) rm -f $(BENCH_DEP) rm -f $(BENCH_CI) rm -f $(BENCH_GCNO) rm -f $(BENCH_GCDA) rm -f $(BENCH_PERF) rm -f $(BENCH_TRACE)