我编写了一个(GNU make
)Makefile,用于在header include中执行自动依赖跟踪。一切都很好除了,第二次输入make
时,整个代码库重建。只有第三次和连续时间键入make
才会显示无需执行任何操作的消息。
SRCDIR := src
INCDIR := inc
ifeq ($(DEBUG),1)
OBJDIR := debug_obj
BINDIR := debug_bin
else
OBJDIR := obj
BINDIR := bin
endif
BINS := prog1 prog2 prog3 prog4
SRCS := $(wildcard $(SRCDIR)/*.cpp)
OBJS := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(SRCS:.cpp=.o))
DEPS := $(OBJS:.o=.d)
CC := g++
COMMON_FLAGS := -Wall -Wextra -Werror -std=c++11 -pedantic
ifeq ($(DEBUG),1)
CXX_FLAGS := $(COMMON_FLAGS) -Og -g
else
CXX_FLAGS := $(COMMON_FLAGS) -O3 -D NDEBUG
endif
all: $(addprefix $(BINDIR)/,$(BINS)) | $(BINDIR)
$(OBJDIR) $(BINDIR):
@ mkdir -p $@;
$(BINDIR)/%: $(OBJDIR)/%.o | $(BINDIR)
$(CC) $(CPP_FLAGS) $< -o $@;
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
$(CC) $(CPP_FLAGS) -MMD -MP -c $< -o $@;
-include $(DEPS)
.PHONY: all clean
clean:
- rm -f $(OBJS);
- rm -f $(DEPS);
- rm -f $(addprefix $(BINDIR)/,$(BINS));
- rmdir $(OBJDIR) $(BINDIR) 2> /dev/null || true
显然,某些依赖项已更改,因此我尝试在make -n -d | grep 'newer'
的第一次调用后运行make
,其中显示了这一点:
先决条件
obj/prog1.o' is newer than target
bin / prog1'。
先决条件obj/prog2.o' is newer than target
bin / prog2'。
先决条件obj/prog3.o' is newer than target
bin / prog3'。
先决条件obj/prog4.o' is newer than target
bin / prog4'。
ls -la obj/*
显示存在依赖项(* .d)但不存在对象(* .o)文件。我假设这与g++ -MMD -MP
的工作方式有关,但尽管明显缺少对象文件,但在第一个make
之后会出现二进制文件。
this question的答案表明两者都是同时生成的,man g++
对此我并不反对。
我已经阅读了其他一些与自动依赖关系跟踪相关的问题和答案,但我没有看到这个问题。为什么会这样?你能建议修复吗?
更仔细地看一下make
的第一次调用,最后显示了这个意外的(对我而言):
rm obj / prog1.o obj / prog2.o obj / prog3.o obj / prog4.o
这回答了一个问题但提出了另一个问题。
我也在调试输出中找到了这个。
Considering target file `prog1'.
File `prog1' does not exist.
make: *** No rule to make target `prog1'. Stop.
No implicit rule found for `prog1'.
Finished prerequisites of target file `prog1'.
Must remake target `prog1'.
我注意到prog1缺少bin /前缀。没有什么能解释为什么第一次运行会删除目标文件,但第二次运行会离开它们。这似乎是问题的核心。
答案 0 :(得分:0)
make
将对象文件视为中间件并相应地删除它们。添加:
.SECONDARY: $(OBJS)
解决了这个问题。我不知道为什么它是第一次调用而不是第二次调用。欢迎提出意见。
答案 1 :(得分:0)
.o
文件不存在的原因是它们被视为中间文件,因此请删除它们。但是,这不应该导致您的构建中出现任何问题,因为只要make可以设想中间文件,它就会意识到如果它的先决条件比其父级更早,则不需要重建它(在这种情况下,只要prog1
比例如prog1.cpp
更新。
我无法重现您的第二次构建重建所有内容的体验。将需要更多细节。您显示的输出并不有趣,因为这只是说make不需要重建.o文件(它比先决条件更新)。您需要在输出中找到解释为什么make 需要重建.o文件的行。如果您提供该信息,我们可能会提供帮助。
对你的makefile只做了几点评论:首先,我认为迫使mkdir
规则始终成功并不是一个好主意。如果mkdir失败,你想要你的构建失败。可能你这样做了所以如果目录已经存在就不会有问题,但这不是必需的,因为mkdir -p
调用永远不会因为目录存在而失败(但如果目录无法创建则会失败)出于其他原因,如权限)。您也可以将这些组合成一个包含多个目标的规则:
$(BINDIR) $(OBJDIR):
@mkdir -p $@
接下来,您不需要在命令行中使用分号,事实上,添加它们会导致构建稍慢。
最后,小编,但编译行中正确的选项顺序是-c -o $@ $<
;源文件不是(这是一个常见的误解)-c
选项的参数。 -c
选项(如-E
,-s
等)告诉编译器要创建哪个输出;在-c
的情况下,它意味着编译成目标文件。这些选项不带参数。文件名是一个单独的参数。