You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:17:31