修改文件后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文件,链接自然成功。
对应问题的答案
- 操作无错误,是Makefile的自动变量使用不当导致的问题。
- 不会导致应用故障:第二次编译成功后生成的程序是完整的,所有必要目标文件都参与了链接。但每次修改文件后需执行两次
make,属于效率与体验问题。 - 项目规模扩大后问题会更严重:文件数量越多,第一次链接缺失的目标文件就越多,不仅会重复出现链接错误,还会浪费时间——每次都要先触发一次失败的链接,再执行第二次完整链接,冗余消耗会随文件数量增加而愈发明显。
修复方法
把链接规则里的$?替换为$^:
$(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
相关产品推荐
相关产品推荐

