Make ignores generated dependencies

2019-02-20 01:18发布

问题:

I'm trying to write a Makefile for my project that automatically generates the dependencies between my source files. The Makefile is as follows:

CC = g++

CFLAGS  = -std=c++11 -Wfatal-errors -fdiagnostics-color=always
LDFLAGS = -lm -lfftw3

SRCDIR = src
OBJDIR = obj
DEPDIR = dep

SRCS = $(wildcard $(SRCDIR)/*.cpp)
OBJS = $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(SRCS)) 
DEPS = $(patsubst $(SRCDIR)/%.cpp,$(DEPDIR)/%.d,$(SRCS))

PROG = whistle_recognition

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) -o$(PROG) $(OBJS) $(LDFLAGS)

.PHONY: clean run

$(DEPS): $(DEPDIR)/%.d : $(SRCDIR)/%.cpp
    $(CC) $(CFLAGS) -MM $< -MF $@

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp 
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -r $(OBJS) $(DEPS) $(PROG) 

run: $(PROG)
    ./$(PROG)

-include $(DEPS)

Generating the dependencies works fine. However, Make ignores them. Suppose I have a file foo.cpp that includes the header file quux.h . If foo.cpp changes Make rebuilds foo.o. However if quux.h changes Make thinks that foo.o is up to date. How can I fix this?

Edit: Heres more info: output of make -d whistle_recognition | grep signalscanner.d:

Reading makefile 'dep/signalscanner.d' (search path) (don't care) (no ~ expansion)...
 Considering target file 'dep/signalscanner.d'.
  File 'dep/signalscanner.d' does not exist.
  Finished prerequisites of target file 'dep/signalscanner.d'.
 Must remake target 'dep/signalscanner.d'.
g++ -std=c++11 -Wfatal-errors -fdiagnostics-color=always -MM src/signalscanner.cpp -MF dep/signalscanner.d
Putting child 0xcda970 (dep/signalscanner.d) PID 2404 on the chain.
Live child 0xcda970 (dep/signalscanner.d) PID 2404 
 Successfully remade target file 'dep/signalscanner.d'.
Reading makefile 'dep/signalscanner.d' (search path) (don't care) (no ~ expansion)...
 Considering target file 'dep/signalscanner.d'.
  Finished prerequisites of target file 'dep/signalscanner.d'.
  Prerequisite 'src/signalscanner.cpp' is older than target 'dep/signalscanner.d'.
 No need to remake target 'dep/signalscanner.d'.

output of make -p whistle_recognition | signalscanner.o(shortened):

[...]
obj/signalscanner.o: src/signalscanner.cpp
[...]
signalscanner.o: src/signalscanner.cpp src/signalscanner.h src/frequencyanalyzer.h src/freqanalyzer_test.h src/wav_file.h src/signalscanner_test.h

And theres the problem: g++ doesn't include the obj/-prefix to the targets... Is there a way to fix this via pattern substitution?

回答1:

People often have such rules for dependency generation, but they are really unnecessary.

The first time a project is built no dependencies are necessary since it builds all sources anyway. It is only the subsequent builds that require the dependencies from the previous build to detect what needs to be rebuilt.

The dependencies are just a by-product of compilation.


The generated dependencies contain paths to corresponding .o files. Since .o output paths were not specified when generating dependencies, those paths are incorrect.

The following solution puts .d files along with corresponding .o files and those .d files contain the correct paths to .o. It also does compilation and dependencies in one pass.

Fixes:

Remove these lines:

$(DEPS): $(DEPDIR)/%.d : $(SRCDIR)/%.cpp
    $(CC) $(CFLAGS) -MM $< -MF $@
-include $(DEPS)

Update these lines:

DEPS = $(OBJS:%.o=%.d)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp 
    $(CC) -c $(CFLAGS) -MD -MP -o $@ $< 

Add these lines at the bottom:

ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif