多归档Makefile问题:编译01_Planet-Geom时符号未定义
问题场景
我有一个存放OpenGL个人项目头文件与源文件的目录,目标是生成toolbox.h和geometry.h两个主头文件(后者包含前者),并将对应源码打包为两个归档文件。执行make 01_Planet-Geom时,虽已将toolbox.a加入构建规则,但toolbox.h中的符号未找到,而make 00_Dyn-6D可正常运行。
相关构建规则
01_Planet-Geom: 01_Planet-Geom.o toolbox.a geometry.a gcc $(CFLG) -o $@.out $^ $(LIBS)
编译报错信息
gcc -c -O3 -Wall -std=gnu17 01_Planet-Geom.c gcc -c -O3 -Wall -std=gnu17 TriNet.c ar -rcs geometry.a toolbox.a TriNet.o gcc -O3 -Wall -std=gnu17 -o 01_Planet-Geom.out 01_Planet-Geom.o toolbox.a geometry.a -lglut -lGLU -lGL -lm /usr/bin/ld: geometry.a(TriNet.o): in function `N_from_VF': TriNet.c:(.text+0x151): undefined reference to `get_CCW_tri_norm' ... /usr/bin/ld: TriNet.c:(.text+0x1abd): undefined reference to `dot_vec4f' collect2: error: ld returned 1 exit status make: *** [makefile:50: 01_Planet-Geom] Error 1
当前Makefile内容
########## COMPILATION SETTINGS ################## CFLG=-O3 -Wall -std=gnu17 LIBS=-lglut -lGLU -lGL -lm CLEAN=rm -f *.out *.o *.a ########## DEPENDENCIES ########################## ### Prog Dependencies ### 00_Dyn-6D.o: 00_Dyn-6D.c toolbox.h 01_Planet-Geom.o: 01_Planet-Geom.c toolbox.h geometry.h ### Float Matrix Math ### matrix4x4f.o: matrix4x4f.c matrix4x4f.h ### OpenGL Toolbox ### OGL_utils.o: OGL_utils.c toolbox.h load_assets.o: load_assets.c toolbox.h vector-f_ops.o: vector-f_ops.c toolbox.h ### Geometry Construction ### TriNet.o: TriNet.c toolbox.h geometry.h ########## ARCHIVES & GENERAL RULES ############## toolbox.a: OGL_utils.o load_assets.o vector-f_ops.o ar -rcs $@ $^ geometry.a: toolbox.a TriNet.o ar -rcs $@ $^ # Compile rules .c.o: gcc -c $(CFLG) $< ########## PROGRAM & CLEAN RULES ################# ##### Program Rules ############################## 00_Dyn-6D: 00_Dyn-6D.o toolbox.a gcc $(CFLG) -o $@.out $^ $(LIBS) 01_Planet-Geom: 01_Planet-Geom.o toolbox.a geometry.a gcc $(CFLG) -o $@.out $^ $(LIBS) ##### Clean Rule ################################# # Clean clean: $(CLEAN)
我已确认所有源文件均正确包含对应头文件,TriNet.c曾在其他环境编译成功,添加相关规则后make 00_Dyn-6D仍可正常运行。
问题原因
核心问题在于**ar工具不会自动递归展开归档文件**:当前构建geometry.a时,直接将toolbox.a作为一个文件打包进去,而非把toolbox.a内部的目标文件(OGL_utils.o、load_assets.o、vector-f_ops.o)合并到geometry.a中。链接阶段,ld处理geometry.a时,只会读取其中的TriNet.o和toolbox.a这个归档文件,但不会主动解压嵌套的toolbox.a去查找符号,导致TriNet.o引用的get_CCW_tri_norm、dot_vec4f等符号无法被解析。
而00_Dyn-6D能正常编译,是因为它直接链接toolbox.a,没有嵌套归档的问题,ld可以直接从toolbox.a中找到所需符号。
解决方案
只需修改geometry.a的构建规则,确保toolbox.a中的目标文件被实际合并到geometry.a中,有两种可行方法:
方法1:直接依赖toolbox的目标文件
跳过嵌套归档,直接将toolbox.a的所有依赖目标文件与TriNet.o一起打包:
geometry.a: OGL_utils.o load_assets.o vector-f_ops.o TriNet.o ar -rcs $@ $^
这种方式最直接,避免了归档嵌套的问题,ar会将所有目标文件打包到geometry.a中,链接时ld能直接找到符号。
方法2:先解压toolbox.a再打包
如果希望保留toolbox.a作为中间产物,可以先解压toolbox.a提取目标文件,再与TriNet.o打包:
geometry.a: toolbox.a TriNet.o # 提取toolbox.a中的所有目标文件 ar -x toolbox.a # 将提取的.o文件和TriNet.o打包为geometry.a ar -rcs $@ OGL_utils.o load_assets.o vector-f_ops.o TriNet.o # 清理临时提取的目标文件 rm -f OGL_utils.o load_assets.o vector-f_ops.o
可选:调整链接顺序(临时 workaround)
如果不想修改归档构建规则,也可以调整链接命令的顺序,将geometry.a放在toolbox.a前面(因为geometry.a依赖toolbox.a,库需要放在依赖它的模块之后):
01_Planet-Geom: 01_Planet-Geom.o geometry.a toolbox.a gcc $(CFLG) -o $@.out $^ $(LIBS)
但这种方法只是绕过问题,并未解决geometry.a嵌套归档的本质问题,优先推荐前两种方法。
内容的提问来源于stack exchange,提问作者James Watson

