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 $@
main depends on
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.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
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.