Makefiles are flexible in many ways, it's a small language in itself.
As I said in a post before, I normally write the same
Makefile over and over, even just for compiling a two or more files. The proposed
Makefile there, however, grew to big while writing the post, since I wanted it to be small, but still have a few convinient features.
Today I want to present you a variant which is tiny, yet compiles your small to medium projects just fine. I'll make use of some of
makes every convenient implicit rules.
The implicit rules of
make for compiling a C file look like this:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH) OUTPUT_OPTION = -o $@ %.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $< %: %.o $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
Making use of these builtin rules, a basic
Makefile can be as simple as:
(make sure the executable target contains at least one object file with the same name, or the executale won't be linked)
CC = gcc SRC = $(wildcard *.c) OBJ = $(SRC:.c=.o) .PHONY: clean foo: foo.o clean: rm -f $(OBJ) foo
While this works, and is a variant of the
Makefile I write for every tiny project, it does not yet track dependencies on headers, or other include files, requiring you to manually clean the project and recompile.
Modern compilers (gcc > 4, clang > 3.0 and ICC > 12.1) support the options
-MP, which will generate dependency files (without using
sed, like before, with only
Neither does it respect the environment variable
CC, nor generate any debug files.
After applying these changes, this is the resulting
CC = gcc DEBUG = -ggdb -O0 -march=native CFLAGS := $(DEBUG) -W -Wall -Wextra -Wpedantic -pedantic -ansi LDLIBS := -lm OUTPUT_OPTION = -MMD -MP -o $@ SRC := $(wildcard *.c) OBJ := $(SRC:.c=.o) DEP := $(SRC:.c=.d) -include $(DEP) .PHONY: clean all: test1 test1: test1.o clean: -rm -f $(OBJ) $(DEP) test1
It now correctly tracks dependencies, respects the
CC environment variable, generates
gdb debugging symbols, and has some default compiler / warning flags.