Looking at back at the C++ projects I've developed, the Makefiles are a mess. To tell you the truth, I copied one a long time ago and basically have been cutting and pasting it haphazardly ever since. I've decided to change that so I've been reading more about how Make actually works and some best practices for using it going forth. As most of the programs I write are not that complicated I've come up with a generic Makefile I can cut and paste ^W^W^W deploy going forth. I'd like some feedback on it.
Any input (except "you should be using cmake or $(OTHER_TRENDY_NEW_TOOL) instead") is welcome but some specific questions that come to mind are:
Have I forgotten to add something?
Conversely, have I added too much?
Is it worth making this a POSIX makefile. Currently I've only tried it with GNU make on Linux but portability is good. What about BSD or Windows make? Or should I just rely on gmake being available pretty much everywhere?
Do I have all the recommended compiler/linker flags for C++? I would like to be able to choose between g++ and clang atleast so options should be portable between both though if there is something good that only one supports I can special case it. (Can I?)
I'm assuming Visual Studio/Windows in general is a whole separate can of worms so I've ignored it for now but if you know how this Makefile could be adapted for Windows development I'd be happy to know.
Thanks in advance.
PROGRAM=someprogram
SRCDIR:=src
INCDIR:=include
DEPDIR:=deps
BUILDDIR:=build
DESTDIR?=
PREFIX?=/usr/local
BINDIR?=bin
SRC:=$(wildcard $(SRCDIR)/*.cc)
OBJECTS:=$(patsubst $(SRCDIR)/%.cc,$(BUILDDIR)/%.o,$(SRC))
DEPFILES:=$(patsubst $(SRCDIR)/%.cc,$(DEPDIR)/%.d,$(SRC))
CXX?=/usr/bin/g++
STRIP?=/usr/bin/strip
INSTALL?=/usr/bin/install
VALGRIND?=/usr/bin/valgrind
CPPFLAGS:=-I$(INCDIR)
CXXFLAGS:=-std=c++17 -Wall -Wextra -Wpedantic -flto ${CXXFLAGS}
LDFLAGS:=-ffunction-sections -fdata-sections -Wl,-gc-sections $(LDFLAGS)
DEPFLAGS=-MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
COMPILE.cc=$(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
LINK.cc=$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
POSTCOMPILE=mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@
all: debug
$(BUILDDIR)/%.o: $(SRCDIR)/%.cc $(DEPDIR)/%.d | $(DEPDIR) $(BUILDDIR)
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
$(BUILDDIR)/%.o: $(SRCDIR)/%.s | $(BUILDDIR)
$(COMPILE.s) $(OUTPUT_OPTION) $<
$(BUILDDIR): ; @mkdir -p $@
$(DEPDIR): ; @mkdir -p $@
$(DEPFILES):
$(PROGRAM): $(OBJECTS)
$(LINK.cc) $(OUTPUT_OPTION) $^
debug: CPPFLAGS += -DDEBUG
debug: CXXFLAGS += -g3
debug: $(PROGRAM)
release: CXXFLAGS += -O2
release: distclean $(PROGRAM)
$(STRIP) --strip-all -R .comment -R .note $(PROGRAM)
memcheck: debug
$(VALGRIND) --suppressions=valgrind.suppressions --quiet --verbose --leak-check=full --show-leak-kinds=all --track-origins=yes --log-file=valgrind.log ./$(PROGRAM)
install: release
$(INSTALL) -m755 -D -d $(DESTDIR)$(PREFIX)/$(BINDIR)
$(INSTALL) -m755 $< $(DESTDIR)$(PREFIX)/$(BINDIR/$(PROGRAM)
clean:
-rm -rf $(BUILDDIR)
-rm -rf $(DEPDIR)
-rm valgrind.log
distclean: clean
-rm $(PROGRAM)
.PHONY: all debug release memcheck install clean distclean
.DELETE_ON_ERROR:
-include $(wildcard $(DEPFILES))
```