CMake应⽤:合并静态库
在实际项⽬中,往往需要将⼀些基础库或者算法库发布出去,但是不同项⽬可能需要⽤到不同的⼦模块,此时为了保持简洁,可能需要合并多个静态库为⼀个。
在笔者的实际⼯作中,合并静态库的需求还是有的,⽽且⼤多数时候都是基于CMake的项⽬,所以希望能够基于不同配置,⾃动合并多个模块的静态库为⼀个,⽅便发布版本和管理。本⽂介绍的就是如何在CMake⼯程中,优雅地完成多个静态库⽬标的合并。
⼀ 合并静态库的⽅法
静态库其实就是⼀些源⽂件被编译成对应机器代码⽂件(.o⽂件)的集合。
在Linux系统中,通过ar命令可以对静态库进⾏各种操作,在MacOS下可以使⽤libtool⼯具。有以下⼏种不同的合并静态库的⽅法。
1 ⽅法1
先使⽤ar把静态库拆解为多个.o⽂件:
ar x liba.a
ar x libb.a
再把所有的.o⽂件打包为⼀个静态库:
ar crs libmerge.a *.o
参数解释:
1. x:拆解静态库⽂件为其包含的内容
2. c:封装.o⽂件为静态库⽂件
3. r:覆盖同名库⽂件或者新创建⽬标库⽂件
4. s:相当于对结果执⾏⼀次ranlib,为静态库的内容添加索引,提⾼访问效率
2 ⽅法2
当然,还有更加简洁的命令:
ar crsT libmerge.a liba.a libb.a
参数T表⽰将后续所有静态库中的.o⽂件打包到第⼀个参数指定的静态库⽂件中,如果不加该参数,得到的将会是后⾯⼏个.a⽂件的集合。可以使⽤命令ar -t查看打包的内容,诸君⼿动⼀试便知。
MacOS下的ar命令和Linux的有所不同,此法不适⽤于MacOS。
3 ⽅法3
使⽤MRI脚本。
⾸先编写⼀个MRI脚本,⽐如i:
create libmerge.a
addlib liba.a
addlib libb.a
save
end
然后使⽤命令:
ar -M < i
MacOS下⾯的的ar命令并没有-M参数,所以此法也不适⽤于MacOS系统。
4 ⽅法4
此⽅法针对MacOS系统,在MacOS系统下可以使⽤libtool命令:
libtool -static -o libmerge.a liba.a libb.a
在Linux下也有libtool⼯具,但是⽤法和MacOS也是不⼀致的,所以此法不适⽤于Linux。
⼆ 基于CMake合并静态库
在⽰例项⽬cmake-template中,源码⽬录下有两个⼦⽬录:src/math和src/nn,分别编译得到两个静态库⽬标libmath.a和libnn.a。
现在在项⽬根⽬录下的中:通过add_custom_command命令配合add_custom_target命令,将libmath.a和libnn.a合并为libmerge.a;并将合并的静态库⽂件导⼊使⽤。
1 合并静态库
这⾥使⽤了CMake的内置变量APPLE,如果是MacOS系统,APPLE会被设置为true,以此来确定要使⽤libtool还是ar。
合并静态库实现代码如下:
# Merge library
if (APPLE)
add_custom_command(OUTPUT libmerge.a
COMMAND libtool -static -o libmerge.a $<TARGET_FILE:math> $<TARGET_FILE:nn>
DEPENDS math nn)
else()
add_custom_command(OUTPUT libmerge.a
COMMAND ar crsT libmerge.a $<TARGET_FILE:math> $<TARGET_FILE:nn>
DEPENDS math nn)
endif()
add_custom_target(_merge ALL DEPENDS libmerge.a)
代码解释:
1. OUTPUT:指定输出⽂件名称(会被标记为GENERATED)
2. COMMAND:后⾯跟的就是要执⾏的命令,这⾥就和在shell中执⾏命令差不多
3. DEPENDS:表明依赖的⽬标
4. add_custom_target指明的⽬标依赖于合并操作的输出(libmerge.a),⽽合并操作需要依赖⽬标math和nn,所以在这两个⽬标⽂件⽣
成以后,就会去执⾏合并操作
需要注意的是,合并静态库的时候需要知道每个静态库的路径,在CMake中,⽬标静态库math的路径可以使⽤⽣成器表达
式$<TARGET_FILE:math>获取。
但是还有另外⼀种情况,如果是使⽤find_library查到的静态库,⽐如:
find_library(LIB_C c HINTS ${SEARCH_PATH})
这时DEPENDS中就不⽤加上c,⽽${LIB_C}就是该库的路径了。
2 导⼊合并的静态库并使⽤
现在把合并的静态库导⼊,并在链接demo可执⾏程序时使⽤。
代码实现如下:
add_library(merge STATIC IMPORTED GLOBAL)
set_target_properties(merge PROPERTIES
cmake如何使用IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/libmerge.a
)
# Build demo executable
add_executable(demo src/c/main.c)
target_link_libraries(demo PRIVATE merge)
因为libmerge.a是命令add_custom_command指定的输出,所以它会标记为是⾃动⽣成的⽂件(GENERATED)。
链接demo的时候依赖导⼊的静态库merge,⽽merge的IMPORTED_LOCATION指定的这个⽂件是⾃动⽣成的,所以CMake就知道需要
等libmerge.a⽣成之后才能开始链接demo。
如果没有add_custom_target那⼀⾏,执⾏编译构建会报错,由此也可见⼀斑:
make[2]: *** No rule to make target `libmerge.a', needed by `demo'
以上便是CMake下合并静态库的实践,欢迎交流指正。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论