-->

Why does this makefile execute a target on 'ma

2020-08-09 10:24发布

问题:

This is my current makefile.

CXX      = g++
CXXFLAGS = -Wall -O3
LDFLAGS  =

TARGET = testcpp
SRCS   = main.cpp object.cpp foo.cpp
OBJS   = $(SRCS:.cpp=.o)
DEPS   = $(SRCS:.cpp=.d)


.PHONY: clean all

all: $(TARGET)

$(TARGET): $(OBJS)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)

.cpp.o:
    $(CXX) $(CXXFLAGS) -c $< -o $@

%.d: %.cpp
    $(CXX) -M $(CXXFLAGS) $< > $@

clean:
    rm -f $(OBJS) $(DEPS) $(TARGET)

-include $(DEPS)

It works perfectly with one exception. If the directory is already clean (no *.d, *.o) and I run 'make clean', it re-creates the dependencies, then immediately deletes them:

[user@server proj]$ make
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
g++ -Wall -O3 -c main.cpp -o main.o
g++ -Wall -O3 -c object.cpp -o object.o
g++ -Wall -O3 -c foo.cpp -o foo.o
g++ -Wall -O3  main.o object.o foo.o -o testcpp
[user@server proj]$ make clean
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user@server proj]$ make clean
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user@server proj]$

I don't understand why the second 'make clean' would re-generate the dependency files. How can I avoid this? This isn't a big deal for this contrived example, but for a large project, it can be quite time-consuming.

Thanks.

回答1:

It's because the .d files are being -included unconditionally. As far as make knows, they could add dependencies or commands to the clean target. All included files are built first for this reason, otherwise you might get an incorrect or failed build. To disable this, you want to conditionally include the dependency files:

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

An alternative solution is to generate the dependency files using touch and have them replaced by actual data as a side-effect of compilation. This is how automake does its dependency tracking, as it makes one-time builds faster. Look into the -MD and -MMD options to gcc if you want to go this route. Use a pattern rule like:

%.d:
    @touch $@

To initially create the dependency files.



回答2:

If you want to skip the include for multiple targets, you can use the filter function.

MAKEFILE_TARGETS_WITHOUT_INCLUDE := clean distclean doc

# Include only if the goal needs it
ifeq ($(filter $(MAKECMDGOALS),$(MAKEFILE_TARGETS_WITHOUT_INCLUDE)),)
  -include $(DEPS)
endif


回答3:

It wants to regenerate the dependency files because it always tries to regenerate all of the makefiles, including -include'd makefiles, before doing anything else. (Well, actually, for me it doesn't do that - I have GNU Make 3.81 - so maybe it's a bug in your version that was fixed, or an optimization that mine has and yours doesn't. But anyway.)

The easiest way around this is to write your rules so they generate the .d files as a side effect of regular compilation, rather than giving explicit rules to generate them. That way, when they're not there, Make doesn't know how to generate them so it doesn't try (in a clean tree, the .cpp.o rules are enough, you don't need the header file dependencies). Look at an Automake-generated makefile -- a simple one -- to see how it's done.



回答4:

The leading - in -include means that make won't complain if the dependencies are missing and can't be remade, but it doesn't mean it won't try to make them first (and, in this case, succeed) -- after all, anything interesting or important could be in the included files, whence the let's-try-making them attempt. I don't think there's a way to stop that.

For docs on include and -include, see here.