How to automatically include header dependencies in makefile

Why automate inclusion of header file dependencies: While working on a new project, I had to write a makefile from scratch. I found it little challenging to find out how to automatically include header dependencies in makefile, because everytime you include some header file in your source file, you can not modify your makefile. So, I thought, there must be some mechanism that automatically takes care of it. If you search on google, you will find various ways. Manual solution is to use sed command that I guess searches for #includes in your source code and puts all those dependencies in your makefile. But with modern compiler, you dont need to do much. It will do your job. You just need to pass some flags to it.


For instance, with gcc version 4.1.2 20080704 (Red Hat 4.1.2-51), before including header dependencies, target to make object file was


./%.o : ../%.cpp
     g++ -c $(INC_PATH) $(FLAGS)  $< -o $@
./%.o : ../%.cpp
     g++ -c $(INC_PATH) $(FLAGS) -MMD -MP $< -o $@

-include $(SRC_FILES:.cpp=.d)

you just need to pass -MMD and _MP flag and need to include these dependencies (.d file).


 See the below example that demonstrate it

Let us say we have 3 c++ and 3 header files :


  1. test.cxx
  2. add.cxx
  3. sub.cxx
  4. test.h
  5. sub.h
  6. add.h
add.cxx includes add.h
sub.cxx includes sub.h
test.cxx includes test.h , add.h and sub.h

Following is the makefile that only includes dependencies of .cxx files  :

---------------------------------------------------------------------------------
### declaration of all variables
FLAGS := -g
COMPILER := g++
SRC_FILES := $(wildcard *.cxx)
OBJ_FILES := $(SRC_FILES:%.cxx=%.o)
.PHONY  : all clean
EXE := my_exe
STATIC_LIB := my_exe.a
all : $(EXE)

### compile source files to make object files
### expand this section for each object file to include dependency of header files
## NAAAA I am not gonna do it..  :P
%.o : %.cxx
        $(COMPILER) -c $(FLAGS) $< -o $@

### make static libary of object files
$(STATIC_LIB) : $(OBJ_FILES)
    ar -crv $@ $^

### make executable  
$(EXE) : $(STATIC_LIB)
    $(COMPILER) $^ -o $@

# clean all the files
clean :
    rm -rf $(OBJ_FILES) $(EXE) $(STATIC_LIB)
---------------------------------------------------------------------------------


If you do any changes in .h file and try to do "make" it will say nothing to be done for 'all'  as we have no where mentioned that any of my target depends on header file.

Hence,  to include dependency of header files I should explicitly include header files in my makefile but what if I add one more header file in test.cxx? I will again need to modify makefile which is a cumbersome task and non-practical in a real project.

Today compiler provides you facility to automatically generate dependency graph. All you need to do is to just pass some flags.

e.g. with g++ compiler you should give -MMD -MP flag. It will generate dependency file(.d) for each object file and include those dependency files in makefile as follow :

---------------------------------------------------------------------------------
## not including variable declaration part

%.o : %.cxx
        $(COMPILER) -c $(FLAGS) -MMD -MP $< -o $@

### include all dependency files

-include $(SRC_FILES:.cxx=.d)

$(STATIC_LIB) : $(OBJ_FILES)
    ar -crv $@ $^
   
$(EXE) : $(STATIC_LIB)
    $(COMPILER) $^ -o $@

clean :
    rm -rf $(OBJ_FILES) $(EXE) $(STATIC_LIB)
---------------------------------------------------------------------------------

Test.d : 


test.o: test.cxx add.h sub.h test.h

add.h:

sub.h:

test.h:

This is how a dependency files look like. Now test.o depends on  test.h, add.h and sub.h along with test.cxx. similarly add.o includes dependency of add.h and sub.o takes sub.h into account.

Whenever time stamp of any of header file changes corresponding object file will be modified that solves our purpose. :)

3 comments:

  1. Great precise explanation. Thanks.

    ReplyDelete
  2. Thanks, really helpful!

    ReplyDelete
  3. Thanks, really helpful!

    ReplyDelete

Thanks for your valuable inputs/feedbacks. :-)