# A Simple General Makefile

February 12, 2015

I have several times made a "general purpose" makefile for C programs - one that can compile programs without having to hard-code the names of files. This is nice because you can add and remove source code files and it will automatically adjust the build rules without requiring you to edit the makefile itself. Writing a makefile like this is not really hard, but the syntax needed is awful and hard to remember. Now that I re-learned how to do it, I'll post it here for my own future reference.

The requirements I have are:

• Headers and source files are in a "source" sub-directory.
• Object files and executables should go in a "bin" sub-directory.
• There is one executable program which depends on all the object files.
• Each object file depends on the source file it is compiled from.
• Each source file depends on all header files. This is conservative as source files that don't include a particular header don't depend on it.

Here is the makefile I came up with that implements this:


# options
CC=clang
CFLAGS=-W -Wall -g
TARGET=bin/program

# globs
SRCS := $(wildcard source/*.c) HDRS :=$(wildcard source/*.h)
OBJS := $(patsubst source/%.c,bin/%.o,$(SRCS))

$(TARGET):$(OBJS) $(HDRS) makefile @mkdir -p bin$(CC) $(CFLAGS)$(OBJS) -o $(TARGET) # compile an object based on source and headers bin/%.o: source/%.c$(HDRS) makefile
@mkdir -p bin
$(CC)$(CFLAGS) -c $< -o$@

# tidy up
clean:
rm -f $(TARGET)$(OBJS)


The options section is for things I might expect to change such as the compiler flags and name of the program.

The "globs" section automatically finds the names of all source and header files from the source directory. It also figures out the names of all the object files by doing a pattern substitution on the names of all sources, replacing "source" with "bin" and ".c" with ".o".

The linking section specifies the program depends on the objects, headers and makefile, and relinks if one of those changes. Putting "makefile" in there makes it so changing the makefile itself (such as the compiler flags) triggers a full re-compile.

The compiling section uses an inference rule to specify an object depends on its source, the headers and the makefile. It then compiles the source from its object. Lastly the clean target deletes the binary files.

This could be adapted for C++ by changing the compiler to clang++ (or g++), and changing .c to .cpp in the rules.

Makefiles are more flexible and powerful than most people realize, but my god the syntax can be painful!