清除目标文件后 Make 不会重新编译

Make doesn't recompile after cleaning object files

我有以下生成文件:

compiler := g++
flags := -Wall -Wextra -Wpedantic -Werror -O2 -march=native
libs := sqlite3

build_dir := build
debug_dir := debug
source_dir := src
object_dir := obj
include_dir := include

objects := main.o politician.o data_base.o exceptions.o input.o
# Prepend object_dir/ to every object
objects := $(patsubst %, $(object_dir)/%, $(objects))

dependencies := data_base.hpp exceptions.hpp politician.hpp input.hpp CLI11.hpp
# Prepend include_dir/ to every dependency
dependencies := $(patsubst %, $(include_dir)/%, $(dependencies))

executable := politician

# Don't remove object files when finished
.SECONDARY: $(objects)

.PHONY: all
all: $(build_dir)/$(executable) | $(build_dir)

.PHONY: debug
debug: flags += -g
debug: $(debug_dir)/$(executable) | $(debug_dir)/

%/$(executable): $(objects)
    $(compiler) $(flags) -l $(libs) $^ -o $@

$(object_dir)/%.o: $(source_dir)/%.cpp $(dependencies) | $(object_dir)/
    $(compiler) $(flags) -I $(include_dir) -c $< -o $@

%/:
    mkdir -p $@

.PHONY: clean
clean:
    rm -f $(objects)

.PHONY: clean-all
clean-all:
    rm -f $(objects) $(build_dir)/$(executable) $(debug_dir)/$(executable)

预计,在 运行 make clean 之后,make all 将重新编译所有内容(因为可执行文件依赖于对象并且它们不再存在),但这不是正在发生的事情:相反,我得到 make: Nothing to be done for 'all'.

是什么导致了这种行为?

发生这种情况是因为您正在使用一系列模式规则。

考虑一个简单的例子:

all: build/politician

build/politician: main.o
    whatever...

main.o: src/main.cpp
    whatever...

如果你 运行 make,它将构建 main.o,然后 build/politician。如果您随后删除 main.o 并再次删除 运行 make,它将再次构建 main.obuild/politician.

现在将其中两个规则更改为模式规则:

all: build/politician

%/politician: main.o
    whatever...

%.o: src/%.cpp
    whatever...

现在您第一次 运行 make,它将再次构建 main.o 然后 build/politician。但是当你删除 main.o 并再次删除 运行 make 时,它会报告 "Nothing to be done for 'all'" 并且什么都不做。这是因为 main.o 现在是一个 中间文件 ,并且根据 the manual

If an ordinary file b does not exist, and make considers a target that depends on b, it invariably creates b and then updates the target from b. But if b is an intermediate file, then make can leave well enough alone. It won’t bother updating b, or the ultimate target, unless some prerequisite of b is newer than that target or there is some other reason to update that target.