Having to deal with dependencies in Makefile is a real pain, there are a lot of examples of way to deal with it on the web but none of them is satisfying.

For example using gcc -MM does not work with subfolders, a depend rule requires the user to use it everytimes he adds new files …

Here is what is wanted:

  • Completly automatic: No user interaction is required when adding new files
  • Works with an arbitrary amount of files
  • Works with an arbitrary amount of level of folders
  • Is not recalculated when nothing changed
  • Use only one file to store dependencies
  • Do not depend on complicated regular expressions

Here is the result:

BINARY  = project.exe
CC      = gcc
CFLAGS  = 
FILES   = $(shell find src/ -name "*.c")
HEADERS = $(shell find src/ -name "*.h")
OBJS    = $(FILES:.c=.o)
 
all: $(BINARY)
 
-include Makefile.deps
 
$(BINARY): Makefile.deps $(OBJS)
        $(CC) $(CFLAGS) $(OBJS) -o $(BINARY)
 
Makefile.deps: $(FILES) $(HEADERS)
        makedepend -- $(CFLAGS) -- $(FILES) -f- > Makefile.deps

all: $(BINARY)

-include Makefile.deps

$(BINARY): Makefile.deps $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(BINARY)

Makefile.deps: $(FILES) $(HEADERS)
makedepend — $(CFLAGS) — $(FILES) -f- > Makefile.deps

This is in fact really easy. In your $(BINARY) rule, you add Makefile.deps as a prerequisite.

In order to generate the Makefile.deps you mark all $(FILES) and $(HEADERS) as prerequisite, so every time you change a file or header it will recompile the list.
We use makedepend to generate the dependencies list. It works like gcc -MM except that it outputs the correct file path when used with folders.

Then all is required is to include the Makefile.deps. We include it with -include so it does work the first time you compile.

Thanks to Lemoine Gauthier who helped me to discover this technique.

If you liked this article, you might be interested in my X feed as well.