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

修改文件后Makefile首次编译报链接错误,二次编译正常

问题描述

我编写了如下Makefile代码:

SOURCES := $(shell find $(SRCDIR) -name *.cpp)
OBJECTS := $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o, $(SOURCES))
DEPFILES := $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.d, $(SOURCES))

-include $(DEPFILES)

#### END CONFIG ####

all: $(TARGET)
    
# LINK AND EXECUTABLE
$(TARGET): $(OBJECTS)
    mkdir -p $(BINDIR)
    $(CXX) -o $@ $? $(SANITIZERS) $(LIBS)
    # Create a backup
    cp $(TARGET) $(TARGET)_`date +%Y%m%d_%H%M%S`
    @echo "Finished executing the target '$@' for project '$(APPNAME)'"

# COMPILE
${OBJDIR}/%.o : $(SRCDIR)/%.cpp 
    mkdir -p $(dir $@) 
    $(CXX) $(FLAGS) $(SANITIZERS) -c $< -o $@  
    @echo "Built '$<' for project '$(APPNAME)'"

其余为目录变量声明及伪目标,未全部展示。

项目处于干净状态时执行make无错误;但修改单个文件后首次编译会出现链接错误:

mkdir -p obj/classes/ 
g++ -Wall -Wextra -g3 -iquoteinc -iquoteinc/classes -iquoteinc/wxwidgets/ -iquoteinc/wxwidgets/panels -MMD -O0 -I/usr/lib/wx/include/gtk3-unicode-3.2 -I/usr/include/wx-3.2 -DWXUSINGDLL -D__WXGTK3__ -D__WXGTK__ -D_FILE_OFFSET_BITS=64 -pthread  -c src/classes/blockcard.cpp -o obj/classes/blockcard.o  
Built 'src/classes/blockcard.cpp' for project 'trellosim'
mkdir -p bin
g++ -o bin/trellosim obj/classes/blockcard.o  -pthread -lwx_gtk3u_xrc-3.2 -lwx_gtk3u_html-3.2 -lwx_gtk3u_qa-3.2 -lwx_gtk3u_core-3.2 -lwx_baseu_xml-3.2 -lwx_baseu_net-3.2 -lwx_baseu-3.2 
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/14.1.1/../../../../lib/Scrt1.o: 在函数‘_start’中:
(.text+0x1b): 对‘main’未定义的引用
collect2: error: ld 返回状态 1
make: *** [Makefile:82: bin/trellosim] Error 1

无任何操作再次执行make则编译成功:

mkdir -p bin
g++ -o bin/trellosim obj/wxwidgets/panels/menupanel.o obj/wxwidgets/panels/rightpanel.o obj/wxwidgets/panels/propertiespanel.o obj/wxwidgets/panels/boardpanel.o obj/wxwidgets/panels/wxblockcardpanel.o obj/wxwidgets/mainwindow.o obj/classes/project.o obj/classes/board.o obj/classes/blockcard.o obj/classes/card.o obj/main.o  -pthread -lwx_gtk3u_xrc-3.2 -lwx_gtk3u_html-3.2 -lwx_gtk3u_qa-3.2 -lwx_gtk3u_core-3.2 -lwx_baseu_xml-3.2 -lwx_baseu_net-3.2 -lwx_baseu-3.2 
# Create a backup
cp bin/trellosim bin/trellosim_`date +%Y%m%d_%H%M%S`
Finished executing the target 'bin/trellosim' for project 'trellosim'

想问三个问题:

  • 我操作上有错误吗?
  • 这个问题会导致应用故障吗?
  • 项目规模扩大后会不会引发更严重的问题?
解答

问题根源

问题出在Makefile的链接规则里:$(CXX) -o $@ $? $(SANITIZERS) $(LIBS) 中使用了$?而非$^。

  • $?代表所有比目标更新的依赖文件:修改单个文件后,只有该文件对应的.o是更新状态,链接时仅传入这一个.o,缺少包含main函数的main.o,导致链接报错。
  • 首次执行make失败后,所有依赖的.o文件其实已存在(干净状态下已编译全量文件,修改仅重新编译单个文件),第二次执行make时,目标$(TARGET)比所有.o都旧,$?会包含所有.o文件,链接自然成功。

对应问题的答案

  1. 操作无错误,是Makefile的自动变量使用不当导致的问题。
  2. 不会导致应用故障:第二次编译成功后生成的程序是完整的,所有必要目标文件都参与了链接。但每次修改文件后需执行两次make,属于效率与体验问题。
  3. 项目规模扩大后问题会更严重:文件数量越多,第一次链接缺失的目标文件就越多,不仅会重复出现链接错误,还会浪费时间——每次都要先触发一次失败的链接,再执行第二次完整链接,冗余消耗会随文件数量增加而愈发明显。

修复方法

把链接规则里的$?替换为$^:

$(TARGET): $(OBJECTS)
    mkdir -p $(BINDIR)
    $(CXX) -o $@ $^ $(SANITIZERS) $(LIBS)
    # Create a backup
    cp $(TARGET) $(TARGET)_`date +%Y%m%d_%H%M%S`
    @echo "Finished executing the target '$@' for project '$(APPNAME)'"

$^会包含所有依赖的目标文件,无论它们是否比目标新,这样每次链接都会使用全量.o文件,避免缺失必要代码的问题。


内容的提问来源于stack exchange,提问作者Infofly

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:53:10