115

Let's say I have a makefile with the rule

%.o: %.c
 gcc -Wall -Iinclude ...

I want *.o to be rebuilt whenever a header file changes. Rather than work out a list of dependencies, whenever any header file in /include changes, then all objects in the dir must be rebuilt.

I can't think of a nice way to change the rule to accomodate this, I'm open to suggestions. Bonus points if the list of headers doesn't have to be hard-coded

Mike
  • 58,961
  • 76
  • 175
  • 221
  • Having written my answer below I looked in the related list and found: http://stackoverflow.com/questions/297514/how-can-i-have-a-makefile-automatically-rebuild-source-files-that-include-a-modif which appears to be a duplicate. Chris Dodd's answer is equivalent to mine, though it uses a different naming convention. – dmckee --- ex-moderator kitten Mar 07 '10 at 00:37

10 Answers10

135

If you are using a GNU compiler, the compiler can assemble a list of dependencies for you. Makefile fragment:

depend: .depend

.depend: $(SRCS)
        rm -f "$@"
        $(CC) $(CFLAGS) -MM $^ -MF "$@"

include .depend

or

depend: .depend

.depend: $(SRCS)
        rm -f "$@"
        $(CC) $(CFLAGS) -MM $^ > "$@"

include .depend

where SRCS is a variable pointing to your entire list of source files.

There is also the tool makedepend, but I never liked it as much as gcc -MM

PiCTo
  • 924
  • 1
  • 11
  • 23
dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • 3
    I like this trick, but how can I get `depend` to run only when the source files have changed? It seems to run every time regardless... – chase Aug 23 '11 at 20:18
  • 2
    @chase: Well, I have erroneously made the dependency on the object files, when it should obviously be on the sources and had the order of dependency wrong for the two targets, too. That's what I get for typing from memory. Try it now. – dmckee --- ex-moderator kitten Aug 23 '11 at 21:15
  • 4
    Is it way to add before every file some prefix to show that it is in another directory e.g `build/file.o` ? – RiaD Oct 12 '12 at 17:41
  • I changed SRCS to OBJECTS, where OBJECTS are a list of my *.o files. That seemed to prevent depend from running every time and also caught changes to the header files only. This seems counter to the previous comments..am I missing something? – BigBrownBear00 Nov 11 '13 at 11:14
  • @dmckee the following question has the same set of issues when dealing with multiple targets and their dependencies? https://stackoverflow.com/questions/30043480/make-recipe-to-prevent-rebuilding-of-non-dependent-targets# – Sami Kenjat May 05 '15 at 05:35
  • This unnecessarily recalculates dependencies for all files whenever any of the sources is modified. Use `-MMD` instead: http://stackoverflow.com/a/30142139/895245 – Ciro Santilli OurBigBook.com Dec 20 '16 at 00:40
  • This also has the advantage over makedpend that it works for non-native gcc compilers as well. In my case avr-gcc. I use cmake for host projects. – Lev Jul 03 '22 at 14:38
109

Most answers are surprisingly complicated or erroneous. However simple and robust examples have been posted elsewhere [codereview]. Admittedly the options provided by the gnu preprocessor are a bit confusing. However, the removal of all directories from the build target with -MM is documented and not a bug [gpp]:

By default CPP takes the name of the main input file, deletes any directory components and any file suffix such as ‘.c’, and appends the platform's usual object suffix.

The (somewhat newer) -MMD option is probably what you want. For completeness an example of a makefile that supports multiple src dirs and build dirs with some comments. For a simple version without build dirs see [codereview].

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

This method works because if there are multiple dependency lines for a single target, the dependencies are simply joined, e.g.:

a.o: a.h
a.o: a.c
    ./cmd

is equivalent to:

a.o: a.c a.h
    ./cmd

as mentioned at: Makefile multiple dependency lines for a single target?

Community
  • 1
  • 1
Sophie
  • 1,374
  • 1
  • 8
  • 12
  • 2
    There is a spelling error in the OBJ variable value: the `CPP` should read `CPPS` – ctrucza Aug 10 '16 at 12:56
  • Thanks @ctrucza, should be fixed now. – Sophie Sep 11 '16 at 21:30
  • 1
    Out of the box, this failed to locate the headers for me even though hpp and cpp are both on the same dir. – villasv Sep 30 '16 at 18:18
  • LOL, it took 5 years for someone to say `-MMD`. Go up sir. But are you sure it is newer? This 1992 commit mentions an `-MMD`: https://github.com/gcc-mirror/gcc/blob/d7a9644ef256c00bfc94f1a7d43c6d288ff21513/gcc/gcc.c but not sure if it is the same thing. – Ciro Santilli OurBigBook.com Dec 20 '16 at 01:20
  • 1
    if you have your source files (`a.cpp`, `b.cpp`) in `./src/`, wouldn't that substitution make `$(OBJ)=./build/src/a.o ./build/src/b.o`? – galois Jan 09 '18 at 21:13
  • 1
    Great answer. As a very very minor (and slightly off-topic!) improvement, I'd suggest using the standard variable names for `GNU make` listed here: https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html. So, CXXFLAGS instead of CXX_FLAGS, and don't you mean LDFLAGS, not CXX_FLAGS for linking? – Matt Wallis Oct 28 '19 at 12:32
  • I don't understand how the *.d files is updated automatically. I mean suppose I remove *.d files and then change some of the dependencies (say some of *.h files). Then when I run `make` nothing's recompiled. Isn't it better to have a rule for the *.d makefiles to be remade? Or is it supposed that we only use `clean` to remove something? – LRDPRDX Feb 13 '21 at 13:39
29

As I posted here gcc can create dependencies and compile at the same time:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

The '-MF' parameter specifies a file to store the dependencies in.

The dash at the start of '-include' tells Make to continue when the .d file doesn't exist (e.g. on first compilation).

Note there seems to be a bug in gcc regarding the -o option. If you set the object filename to say obj/_file__c.o then the generated file.d will still contain file.o, not obj/_file__c.o.

Community
  • 1
  • 1
Martin Fido
  • 1,070
  • 11
  • 12
  • 4
    When I try this it results in all of my .o files being created as empty files. I do have my objects in a build subfolder (so $OBJECTS contains build/main.o build/smbus.o build/etc...) and that certainly creates the .d files as you described with the apparent bug, but it certainly is not building the .o files at all, whereas it does if I remove the -MM and -MF. – bobpaul Nov 14 '12 at 01:03
  • 1
    Using -MT will resolve the note in the last lines of your answer which updates the target of each dependency list. – Godric Seer Mar 22 '13 at 19:47
  • 4
    @bobpaul because `man gcc` says `-MM` implies `-E`, which "stops after preprocessing". You need `-MMD` instead: http://stackoverflow.com/a/30142139/895245 – Ciro Santilli OurBigBook.com Dec 20 '16 at 00:41
23

How about something like:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

You could also use the wildcards directly, but I tend to find I need them in more than one place.

Note that this only works well on small projects, since it assumes that every object file depends on every header file.

Michael Williamson
  • 11,308
  • 4
  • 37
  • 33
  • 18
    This works, however, the problem with this is that every object file gets recompiled, every time a small change is made, ie, if you have 100 source / header files, and you make a small change to only one, all 100 get recompiled. – Nicholas Hamilton Jun 16 '14 at 12:37
  • 2
    This is a very bad solution. Sure it will work on a small project, but for any production size team and build, this will lead to terrible compilation time and become the equivalent of running `make clean all` every time. – Julien Guertault Mar 16 '16 at 02:53
  • In my test, this doesn't work at all. The `gcc` line is not executed at all, but the built-in rule (`%o: %.c` rule) is executed instead. – Penghe Geng Apr 25 '19 at 21:54
6

Martin's solution above works great, but does not handle .o files that reside in subdirectories. Godric points out that the -MT flag takes care of that problem, but it simultaneously prevents the .o file from being written correctly. The following will take care of both of those problems:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<
michael
  • 827
  • 2
  • 10
  • 11
3

This will do the job just fine , and even handle subdirs being specified:

    $(CC) $(CFLAGS) -MD -o $@ $<

tested it with gcc 4.8.3

g24l
  • 3,055
  • 15
  • 28
3

Here's a two-liner:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

This works with the default make recipe, as long as you have a list of all your object files in OBJS.

tbodt
  • 16,609
  • 6
  • 58
  • 83
2

A slightly modified version of Sophie's answer which allows to output the *.d files to a different folder (I will only paste the interesting part that generates the dependency files):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

Note that the parameter

-MT $@

is used to ensure that the targets (i.e. the object file names) in the generated *.d files contain the full path to the *.o files and not just the file name.

I don't know why this parameter is NOT needed when using -MMD in combination with -c (as in Sophie's version). In this combination it seems to write the full path of the *.o files into the *.d files. Without this combination, -MMD also writes only the pure file names without any directory components into the *.d files. Maybe somebody knows why -MMD writes the full path when combined with -c. I have not found any hint in the g++ man page.

MaximumFPS
  • 141
  • 1
  • 4
1

I prefer this solution, over the accepted answer by Michael Williamson, it catches changes to sources+inline files, then sources+headers, and finally sources only. Advantage here is that the whole library is not recompiled if only a a few changes are made. Not a huge consideration for a project with a couple of files, bur if you have 10 or a 100 sources, you will notice the difference.

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)
Nicholas Hamilton
  • 10,044
  • 6
  • 57
  • 88
  • 2
    This works only if you do not have anything in your header files that would require recompilation of any cpp-files other then the corresponding implementation file. – matec Nov 04 '14 at 10:28
1

The following works for me:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
  • 1
    summary: `-MMD`: will create a .d file, which looks like this: "A.o: B.cpp C.hpp D.hpp". this will store the object file and the .cpp with it depends as well as the .hpp files. THIS IS THE TRICK, which makes the binary be generated again when .hpp changes. `-include`: https://www.gnu.org/software/make/manual/html_node/Include.html, which will include the "A.o: B.cpp C.hpp D.hpp" string, creating a multiline rule. – Caio V. Feb 20 '22 at 17:48