如何让Makefile检测已编译文件,避免重复编译?
问题描述
将Sources目录及其子目录下的所有*.cpp文件编译至Objects目录,目前编译可正常执行,但每次运行make时都会重新编译所有文件。我的Makefile内容如下:
CXX := g++ CXXFLAGS := -IIncludes -std=c++20 -Wall -Wextra LDFLAGS := -Llib OBJDIR := Objects RMDIR := rmdir SDLFLAGS := -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf # Switch between Windows and Linux ifeq ($(OS), Windows_NT) SRCS := $(shell dir /S /B Sources\*.cpp) FixPath = $(subst /,\,$1) RM := del /Q else SRCS := $(shell find Sources/ -type f -name '*.cpp') FixPath = $1 RM := rm endif EXEC := my_exec.exe OBJS := $(SRCS:.cpp=.o) COMP := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(SRCS))) all: $(EXEC) $(OBJDIR): mkdir $(OBJDIR) %.o: %.cpp | $(OBJDIR) $(CXX) $(CXXFLAGS) -c -o $(OBJDIR)/$(notdir $@) $< ifeq ($(OS), Windows_NT) $(EXEC): $(OBJS) | $(SRCS) $(CXX) $(CXXFLAGS) -o $@ $(COMP) $(LDFLAGS) -lmingw32 $(SDLFLAGS) else $(EXEC): $(OBJS) | $(COMP) $(CXX) $(CXXFLAGS) -o $@ $(COMP) $(LDFLAGS) $(SDLFLAGS) endif clean: $(RM) $(call FixPath, $(COMP)) $(RMDIR) $(OBJDIR) fclean: clean $(RM) $(call FixPath, $(EXEC)) re: fclean all .PHONY: all clean fclean re $(EXEC)
尝试过使用顺序依赖,但无法让其检测到已更新的.o文件。请问能否在保持编译文件与对应.cpp文件处于不同目录的前提下实现该功能?
解决方案
问题根源在于原Makefile的依赖关系与实际生成的文件不匹配:OBJS变量指向Sources目录下的.o文件(实际并未生成),导致make认为这些依赖始终缺失,每次都会触发全量编译;同时模式规则的目标定义错误,无法正确追踪目标文件与源文件的修改时间。
以下是修正后的Makefile,可实现增量编译且保持目标文件在Objects目录:
CXX := g++ CXXFLAGS := -IIncludes -std=c++20 -Wall -Wextra LDFLAGS := -Llib OBJDIR := Objects RMDIR := rmdir SDLFLAGS := -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf # Switch between Windows and Linux ifeq ($(OS), Windows_NT) SRCS := $(shell dir /S /B Sources\*.cpp) FixPath = $(subst /,\,$1) RM := del /Q MKDIR := mkdir else SRCS := $(shell find Sources/ -type f -name '*.cpp') FixPath = $1 RM := rm -f MKDIR := mkdir -p endif EXEC := my_exec.exe # 定义实际生成的目标文件路径:Objects/xxx.o COMP := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(SRCS))) all: $(EXEC) $(OBJDIR): $(MKDIR) $(call FixPath, $(OBJDIR)) # 模式规则:明确目标为Objects目录下的.o文件,依赖为对应的.cpp源文件 $(OBJDIR)/%.o: %.cpp | $(OBJDIR) $(CXX) $(CXXFLAGS) -c -o $@ $< # 可执行文件依赖改为实际存在的目标文件 ifeq ($(OS), Windows_NT) $(EXEC): $(COMP) $(CXX) $(CXXFLAGS) -o $@ $(COMP) $(LDFLAGS) -lmingw32 $(SDLFLAGS) else $(EXEC): $(COMP) $(CXX) $(CXXFLAGS) -o $@ $(COMP) $(LDFLAGS) $(SDLFLAGS) endif clean: $(RM) $(call FixPath, $(COMP)) # 加-号避免目录非空时make报错 -$(RMDIR) $(call FixPath, $(OBJDIR)) fclean: clean $(RM) $(call FixPath, $(EXEC)) re: fclean all .PHONY: all clean fclean re
关键修改点说明:
- 移除无用的
OBJS变量:原变量指向不存在的文件,无实际作用。 - 修正模式规则:
$(OBJDIR)/%.o: %.cpp让make明确目标文件的位置,能正确对比源文件与目标文件的修改时间,实现增量编译。 - 调整可执行文件依赖:将
$(EXEC)的依赖改为实际生成的$(COMP)变量,只有当目标文件缺失或源文件更新时,才会重新编译对应文件并链接。 - 优化目录操作:Linux下用
mkdir -p避免目录已存在时报错;clean中删除目录前加-,忽略目录非空的错误。 - 移除
.PHONY中的$(EXEC):可执行文件是实际生成的文件,无需标记为伪目标。
内容的提问来源于stack exchange,提问作者Sunchock
相关产品推荐
相关产品推荐

