我有以下makefile:
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.o
和build/politician
。
现在将其中两个规则更改为模式规则:
all: build/politician
%/politician: main.o
whatever...
%.o: src/%.cpp
whatever...
现在第一次运行make
时,它将再次构建main.o
,然后再构建build/politician
。但是,当您删除main.o
并再次运行make
时,它将报告“对于'all'无需执行任何操作”并且什么也不做。这是因为main.o
现在是中间文件,并且根据the manual:
如果普通文件b不存在,并且make考虑依赖于b的目标,则它总是创建b,然后从b更新目标。但是,如果b是一个中间文件,那么make可以单独放置。除非更新b的某些先决条件或有其他原因更新该目标,否则它不会麻烦更新b或最终目标。