CMake应⽤:模块化及库依赖
当项⽬⽐较⼤的时候,往往需要将代码划分为⼏个模块,可能还会分离出部分通⽤模块,在多个项⽬之间同时使⽤;当然,也可能是依赖开源的第三⽅库,在项⽬中包含第三⽅源代码或者编译好的库⽂件。本⽂将会介绍CMake中如何模块化地执⾏编译,以及指定⽬标对相应库⽂件的依赖。
在上⼀篇⽂章中,笔者介绍了⼀个⽐较完备的该如何书写。往期⽂章可以关注本号的话题:CMake,⽂章列表如下(⽂末可连续阅读):
但是上⼀篇⽂章介绍的⼀般是在项⽬初期的样⼦,随着项⽬代码原来越多,或者功能越来越多,代码可能会分化出不同的功能模块,并且有⼀些可能是多个项⽬通⽤的模块,这时为了更好地管理各个模块,可以为每个模块都编写⼀个⽂件,然后在⽗级⽬录中对不同编译⽬标按需添加依赖。
本⽂着重介绍下⾯的内容:
1. 模块化管理构建系统(add_subdirectory)
2. 导⼊编译好的⽬标⽂件
3. 添加库依赖
⼀ 模块化构建
在前⾯的⽂章中介绍过,是定义⼀个⽬录(Source Tree)的构建系统的,所以对于模块化构建,其实就是分别为每⼀个⼦模块⽬录编写⼀个,在其⽗⽬录中“导⼊”⼦⽬录的构建系统⽣成对应的⽬标,以便在⽗⽬录中使⽤。
下⾯仍以开源项⽬:gitee/RealCoolEngineer/cmake-template为例,基于上⼀篇⽂章的状态进⾏修改,本⽂对应的commit id为:4bfb85b。
假设项⽬⽬录结构如下:
./cmake-template
├──
├── src
│└── c
│├── cmake_template_version.h
│├── cmake_template_version.h.in
│├── main.c
│└── math
│├── add.c
│├── add.h
│├── minus.c
│└── minus.h
└── test
└── c
├── test_add.c
└── test_minus.c
现在的编译任务为:
1. 将math⽬录视为⼦模块,为其单独定义构建系统
2. 整个项⽬依赖math模块的编译结果,⽣成其他⽬标⽂件
1 定义⼦⽬录的构建系统
只要是定义⽬录的构建系统,都是在此⽬录下创建⼀个⽂件,其结构和语法在上⼀篇⽂章已经介绍的⽐较详细。
因为主要进⾏模块的编译⼯作,所以⼀般只需要编译构建库⽂件(静态库或者动态库),以及针对该库对外提供接⼝的⼀些单元测试即可,所以可以写的⽐较简单⼀些。
在src/math⽬录下新建⽂件,内容如下:
cmake_minimum_required(VERSION 3.12)
project(CMakeTemplateMath VERSION 0.0.1 LANGUAGES C CXX)
aux_source_directory(. MATH_SRC)
message("MATH_SRC: ${MATH_SRC}")
add_library(math STATIC ${MATH_SRC})
如上代码所⽰,对于⼦⽬录(模块),⼀般也有⾃⼰的project命令,同时如果有需要,也可以指定⾃⼰的版本号。
这⾥使⽤了⼀个此前没有提到的命令:aux_source_directory,该命令可以搜索指定⽬录(第⼀个参数)下的所有源⽂件,将源⽂件的列表保存到指定的变量(第⼆个参数)。
2 包含⼦⽬录
通过命令add_subdirectory包含⼀个⼦⽬录的构建系统,其命令格式如下:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
其中source_dir就是要包含的⽬标⽬录,该⽬录下必须存在⼀个⽂件,⼀般为相对于当前的⽬录路径,当然也可以是绝对路径;
binary_dir是可选的参数,⽤于指定⼦构建系统输出⽂件的路径,相对于当前的Binary tree,同样也可以是绝对路径。⼀般情况
下,source_dir是当前⽬录的⼦⽬录,那么binary_dir的值为不做任何相对路径展开的source_dir;但是如果source_dir不是当前⽬录的⼦⽬录,则必须指定binary_dir,这样CMake才知道要将⼦构建系统的相关⽂件⽣成在哪个⽬录下。
如果指定了EXCLUDE_FROM_ALL选项,在⼦路径下的⽬标默认不会被包含到⽗路径的ALL⽬标⾥,并且也会被排除在IDE⼯程⽂件之外。但是,如果在⽗级项⽬显式声明依赖⼦⽬录的⽬标⽂件,那么对应的⽬标⽂件还是会被构建以满⾜⽗级项⽬的依赖需求。
综上,可以修改cmake-template项⽬根⽬录下的⽂件,将原来的如下内容:
# Build math lib
add_library(math STATIC ${MATH_LIB_SRC})
修改为:
add_subdirectory(src/c/math)
构建的静态库的名字依旧是math,所以在编译demo⽬标时,链接的库的名字不⽤修改:
# Build demo executable
add_executable(demo src/c/main.c)
target_link_libraries(demo math)
此时构建和编译的命令没有任何改变:
➜ cmake-template # cmake -B cmake-build
➜ cmake-template # cmake --build cmake-build
上⾯的命令指定⽗项⽬的⽣成路径(Binary tree)为cmake-build,那么⼦模块(math)的⽣成路径为cmake-build/src/c/math,也就是
说binary_dir为src/c/math,等同于source_dir。
⼆ 导⼊编译好的⽬标⽂件
在前⾯介绍的命令add_subdirectory其实是相当于通过源⽂件来构建项⽬所依赖的⽬标⽂件,但是CMake也可以通过命令来导⼊已经编译好的⽬标⽂件。
1 导⼊库⽂件
使⽤add_library命令,通过指定IMPORTED选项表明这是⼀个导⼊的库⽂件,通过设置其属性指明其路径:
add_library(math STATIC IMPORTED)
set_property(TARGET math PROPERTY
IMPORTED_LOCATION "./lib/libmath.a")
对于库⽂件的路径,也可以使⽤find_library命令来查,⽐如在lib⽬录下查math的Realse和Debug版本:
find_library(LIB_MATH_DEBUG mathd HINTS "./lib")
find_library(LIB_MATH_RELEASE math HINTS "./lib")
对于不同的编译类型,可以通过IMPORTED_LOCATION_<CONFIG>来指明不同编译类型对应的库⽂件路径:
add_library(math STATIC IMPORTED GLOBAL)
set_target_properties(math PROPERTIES
IMPORTED_LOCATION "${LIB_MATH_RELEASE}"
IMPORTED_LOCATION_DEBUG "${LIB_MATH_DEBUG}"
IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
)
导⼊成功以后,就可以将该库链接到其他⽬标上,但是导⼊的⽬标不可以被install。
这⾥以导⼊静态库为例,导⼊动态库或其他类型也是类似的操作,只需要将⽂件类型STATIC修改成对应的⽂件类型即可。
2 导⼊可执⾏⽂件
这个不是那么常⽤,为了⽂章完整性,顺便提⼀下。是和导⼊库⽂件类似的:
add_executable(demo IMPORTED)
set_property(TARGET demo PROPERTY
IMPORTED_LOCATION "./bin/demo")
三 库依赖
这⾥主要着重介绍⼀下target_link_libraries命令的⼏个关键字:
cmake如何使用1. PRIVATE
2. INTERFACE
3. PUBLIC
这三个关键字的主要作⽤是指定的是⽬标⽂件依赖项的使⽤范围(scope),所以可以专门了解⼀下。
假设某个项⽬中存在两个动态链接库:动态链接库liball.so、动态链接库libsub.so。
对于PRIVATE关键字,使⽤的情形为:liball.so使⽤了libsub.so,但是liball.so并不对外暴露libsub.so的接⼝:
target_link_libraries(all PRIVATE sub)
target_include_directories(all PRIVATE sub)
对于INTERFACE关键字,使⽤的情形为:liball.so没有使⽤libsub.so,但是liball.so对外暴露libsub.so的接⼝,也就是liball.so的头⽂件包含了libsub.so的头⽂件,在其它⽬标使⽤liball.so的功能的时候,可能必须要使⽤libsub.so的功能:
target_link_libraries(all INTERFACE sub)
target_include_directories(all INTERFACE sub)
对于PUBLIC关键字(PUBLIC=PRIVATE+INTERFACE),使⽤的情形为:liball.so使⽤了libsub.so,并且liball.so对外暴露了libsub.so的接⼝:
target_link_libraries(all PUBLIC sub)
target_include_directories(all PUBLIC sub)
这⾥的内容可以有个⼤概了解即可,随着后续深⼊使⽤,⾃然会⽔到渠成。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论