静态库重复符号问题:同源码生成重复目标文件的优化方案
高效识别同源重复目标文件,解决静态库重复符号问题
方法一:利用目标文件命名规则直接分组(最快)
如果目标文件命名遵循固定规则(比如abc_a.o、xyz_a.o对应同一份a.c),可以直接通过Make的字符串操作提取同源文件标识,每组只保留一个目标文件,完全避免调用外部哈希工具:
OBJECT_FILES := kvctmp1934.o kvctmp2160.o kvctmp1932.o kvctmp1931.o kvctmp1939.o .... # 提取每个目标文件对应的源文件基准名(去掉模块前缀和.o后缀) # 假设目标文件名格式为 <模块前缀>_<源文件主名>.o SRC_BASES := $(sort $(foreach obj,$(OBJECT_FILES),$(patsubst %_%.o,%,$(subst _, ,$(basename $(obj)))))) # 为每个源基准名挑选一个目标文件(这里优先取第一个匹配的,也可以指定模块前缀优先) UNIQUE_OBJECTS := $(foreach base,$(SRC_BASES),$(firstword $(filter %_$(base).o,$(OBJECT_FILES)))) foo.a: $(UNIQUE_OBJECTS) ar rcs $@ $^
优点:纯Make内部操作,无外部命令调用,速度极快,适合大规模项目。
缺点:依赖固定的目标文件命名规范。
方法二:读取目标文件的源文件信息分组(不依赖命名规则)
如果命名规则不固定,可以通过读取目标文件的调试信息获取源文件路径,再按源文件分组去重,比MD5哈希快得多(仅读取小部分调试段,而非整个文件):
OBJECT_FILES := kvctmp1934.o kvctmp2160.o kvctmp1932.o kvctmp1931.o kvctmp1939.o .... # 读取每个目标文件对应的源文件路径,按源文件去重后保留一个目标文件 UNIQUE_OBJECTS := $(shell for obj in $(OBJECT_FILES); do \ src_path=$$(readelf -p .debug_line "$$obj" | grep -m1 -o '/.*\.c'); \ echo "$$src_path $$obj"; \ done | sort -k1,1 | uniq -f1 | awk '{print $$2}') foo.a: $(UNIQUE_OBJECTS) ar rcs $@ $^
注意:需要编译时添加-g参数生成调试信息,否则.debug_line段不存在。
优点:不依赖命名规则,通用性强。
缺点:需要调试信息,速度略慢于方法一,但远快于MD5哈希。
方法三:从编译流程优化(根源解决)
最彻底的方案是修改编译规则,让共享源文件只编译一次,两个模块复用同一目标文件,从根源上避免重复:
# 定义共享源文件列表 SHARED_SRCS := a.c b.c c.c ... # 编译共享目标文件(仅编译一次) SHARED_OBJS := $(SHARED_SRCS:.c=.o) # abc模块目标文件:排除共享源文件的重复编译,直接复用SHARED_OBJS ABC_OBJS := $(filter-out $(SHARED_SRCS:.c=.o),$(ABC_ALL_OBJS)) $(SHARED_OBJS) # xyz模块目标文件同理 XYZ_OBJS := $(filter-out $(SHARED_SRCS:.c=.o),$(XYZ_ALL_OBJS)) $(SHARED_OBJS) # 生成静态库时,目标文件无重复 foo.a: $(ABC_OBJS) $(XYZ_OBJS) ar rcs $@ $^
优点:既解决了静态库重复符号问题,又减少了重复编译时间,适合长期维护。
缺点:需要调整现有编译流程,梳理共享源文件列表。
内容的提问来源于stack exchange,提问作者vam
相关产品推荐
相关产品推荐

