3

I am attempting to write a makefile that is able to determine when headers were changed and then recompile the corresponding .cpp files. To test this, I created three files: main.cpp, a.h, and b.h. main.cpp include sa.h and a.h includes b.h.

My makefile looks like this:

prog: main.cpp a.h
        g++ main.cpp -o prog

a.h: b.h

When any combination of a.h, b.h, and main.cpp is changed, I expect prog to be recompiled. Despite this, prog is only recompiled when a.h or main.cpp is changed, with the last line seeming to be ignored.

What am I doing incorrectly and how can I accomplish what I want without adding the full and complete set of headers to each individual .cpp file like this (since for larger projects this could become extremely cumbersome):

prog: main.cpp a.h b.h
        g++ ...
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
john01dav
  • 1,842
  • 1
  • 21
  • 40
  • What is the purpose of adding this line `a.h:b.h`. Are you trying to say that `a.h` depends on `b.h` ? If so does a.h `#inlcude b.h`? – Rishi Jan 28 '17 at 06:01
  • Possible duplicate: https://stackoverflow.com/questions/2394609/makefile-header-dependencies https://stackoverflow.com/questions/15857837/compile-headers-dependencies-with-makefile https://stackoverflow.com/questions/297514/how-can-i-have-a-makefile-automatically-rebuild-source-files-that-include-a-modi – celtschk Jan 28 '17 at 06:02
  • @Rishi You are correct in that a.h includes b.h. – john01dav Jan 28 '17 at 06:13
  • @celtschk From those questions I seemed to gather that makepp does what I want (it works with my makefile as intended). Perhaps that would make a good answer here? I am not good enough in make to determine if the more complicated syntax in your linked questions is equivalent to mine. – john01dav Jan 28 '17 at 06:13
  • by placing the header file name as one of the dependencies in the `prog: ...` recipe, the `make` utility will look at the time stamp of the file and re-run the compile and link step. So the makefile needs nothing special to cause that to happen – user3629249 Jan 28 '17 at 09:51

1 Answers1

6

What am I doing incorrectly

Your rule:

a.h: b.h

merely tells make that a.h depends on b.h, i.e. that a.h will need to be (re)made, in whatever manner make can determine from the makefile, if a.h is older than b.h or doesn't exist.

It doesn't tell make what to do to remake a.h from b.h. Your makefile contains no recipe for remaking a.h from b.h. It only contains a recipe for remaking prog from main.cpp and a.h, namely:

prog: main.cpp a.h
        g++ main.cpp -o prog

Furthermore make, of course, has no builtin rule with a recipe for making a.h from b.h. So in the absence of any recipe for making a.h from b.h it assumes that this dependency requires nothing to be done. There is no other reasonable default. So even if a.h is older than b.h, nothing is done to a.h; and although prog depends on a.h, nothing need be done to prog on that account.

This is fortunate, because in fact you don't want a.h to be remade in any manner whatever when b.h changes, and you don't want main.cpp to be remade in any manner whatever when a.h or b.h changes. You want prog to be remade when any of them changes. What you want is expressed by any of the following makefiles:

1

prog: main.cpp a.h b.h
    g++ main.cpp -o prog

2

prog: main.cpp a.h
    g++ main.cpp -o prog

prog: b.h

3

prog: main.cpp b.h
    g++ main.cpp -o prog

prog: a.h

4

prog: main.cpp
    g++ main.cpp -o prog

prog: a.h b.h

5

prog: main.cpp
    g++ main.cpp -o prog

prog: a.h
prog: b.h

(and some more). They're all equivalent. They all say that prog depends on main.cpp, a.h and b.h and they all say what is to be done whenever prog needs to remade, namely:

    g++ main.cpp -o prog

how can I accomplish what I want without adding the full and complete set of headers to each individual .cpp file like this (since for larger projects this could become extremely cumbersome)

Indeed it would, and for that reason GCC compilers have for a very long time had a feature for generating mini-makefiles that express the dependency of the object file that is going to be generated upon each of the header files that are read in order to make the object file. GNU make can exploit this feature to generate those dependency files and include them in a makefile for building GCC target(s). This co-operation between GCC and make is called auto-dependency generation (or similar). The question of how to do it in a makefile is a duplicate of this one and if you google, e.g. "gcc auto generate dependencies" you can also find guru treatments.

In a comment you suggest that you aren't yet sufficiently expert with GNU make to be confident with the auto-dependency generation techniques illustrated in those answers. Well, you can begin to get the hang of it with a rudimentary implementation as simple as this (which also makes the makefile more normal in other respects):

Makefile

.PHONY: all clean

all: prog

prog: prog.o

prog.o: main.cpp
    g++ -MMD -c -o prog.o main.cpp

prog: prog.o
    g++ -o prog prog.o 

clean:
    rm -f prog *.o *.d

-include prog.d

-MMD is the GCC preprocessor option that generates the dependency file prog.d. Here is the documentation of -MMD

prog.d is a mini-makefile:

$ cat prog.d 
prog.o: main.cpp a.h b.h

expressing all of the dependencies of prog.o. The first time this runs, the include-ed makefile prog.d will not exist, which would be a fatal make error but for the fact that the - prefix tells make to ignore that error. So make proceeds and everything, including prog.d gets made, and thereafter prog.d will be regenerated whenever any rule - including the rule in prog.d itself - requires prog.o to be recompiled.

Community
  • 1
  • 1
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182