Build template
I don’t like make.
The syntax is clunky, the semantics are unintuitive, it’s hard to get parallel builds to work well, and there are a million ways to shoot yourself in the foot.
Nevertheless, I stick with it, because it’s ubiquitous and it’s the simplest solution for what I do.
A simple Makefile might look like this:
all: main
OBJS = \
Foo.o \
Bar.o \
Baz.o
main: $(OBJS)
$(CXX) $(LDFLAGS) $^ -o $@
This says main
depends on Foo.o
, Bar.o
, and Baz.o
; each of these is built using one of the builtin rules.
I often want some extra build flags for debugging or optimization, so I add:
CFLAGS += -ggdb
CXXFLAGS += -fno-inline
If I’m using GCC and I want automatic dependency generation, I add this:
CFLAGS += -MMD -MP
DEP_FILES = $(patsubst %.o,%.d,$(OBJS))
-include $(DEP_FILES)
Now whenever I build Foo.o from Foo.cpp
(or Foo.c
), g++ will generate Foo.d at the same time. The next time I type make
, it will read Foo.d to determine what Foo.o depends on.
And just in case something gets borked, I usually want a clean
rule:
GENERATED_FILES += $(OBJS)
.PHONY: clean
clean:
$(RM) $(GENERATED_FILES)
Now none of this is intuitive (I learned it all from trial-and-error), and there’s a whole lot more that build systems need to do (building shared libraries, finding where libraries are located on a system, etc.), but this really does solve 90% of the problems I hit in small-to-medium projects. I’ve even used a setup like this in a medium-large project (> 200K lines of code) with success.
Please, tell me why I should switch? I despise make just as much as the next guy, but it’s simple, and it works.
Comments