如果项目很大,或者项目中有很多的源码目录,在通过CMake管理项目的时候如果只使用一个CMakeLists.txt
,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt
文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。
先来看一下下面的这个的目录结构:
众所周知,Linux的目录是树状结构,所以嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt
文件变量作用域的一些信息:
根节点CMakeLists.txt
中的变量全局有效
父节点CMakeLists.txt
中的变量可以在子节点中使用
子节点CMakeLists.txt
中的变量只能在当前节点中使用
接下来我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到一个 CMake 命令:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
source_dir
:指定了CMakeLists.txt
源文件和代码文件的位置,其实就是指定子目录
binary_dir
:指定了输出文件的路径,一般不需要指定,忽略即可。
EXCLUDE_FROM_ALL
:在子路径下的目标默认不会被包含到父路径的ALL
目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。
通过这种方式CMakeLists.txt
文件之间的父子关系就被构建出来了。
在上面的目录中我们要做如下事情:
通过 test1 目录中的测试文件进行计算器相关的测试
通过 test2 目录中的测试文件进行排序相关的测试
现在相当于是要进行模块化测试,对于calc
和sort
目录中的源文件来说,可以将它们先编译成库文件(可以是静态库也可以是动态库)然后在提供给测试文件使用即可。库文件的本质其实还是代码,只不过是从文本格式变成了二进制格式。
根目录中的 CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.15)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
#执行add_subdirectory命令时马上执行calc目录下的cmake
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
在根节点对应的文件中主要做了两件事情:定义全局变量和添加子目录。
定义的全局变量主要是给子节点使用,目的是为了提高子节点中的CMakeLists.txt
文件的可读性和可维护性,避免冗余并降低出差的概率。
一共添加了四个子目录,每个子目录中都有一个CMakeLists.txt
文件,这样它们的父子关系就被确定下来了。
calc 目录中的 CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.15)
project(CALCLIB)
#将./目录下搜索到的源文件列表存储到SRC变量中
aux_source_directory(./ SRC)
#指定源文件所包含的头文件,HEAD_PATH是根目录下CMakeLists.txt下定义的头文件目录
include_directories(${HEAD_PATH})
#设置库文件输出路径
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
#在库文件输出路径下生成calc的静态库文件
add_library(${CALC_LIB} STATIC ${SRC})
aux_source_directory
:搜索当前目录(calc目录)下的所有源文件
include_directories
:包含头文件路径,HEAD_PATH
是在根节点文件中定义的
set
:设置库的生成的路径,LIB_PATH
是在根节点文件中定义的
add_library
:生成静态库,静态库名字CALC_LIB
是在根节点文件中定义的
sort 目录中的 CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.15)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
add_library
:生成动态库,动态库名字SORT_LIB
是在根节点文件中定义的
这个文件中的内容和calc
节点文件中的内容类似,只不过这次生成的是动态库。
在生成库文件的时候,这个库可以是静态库也可以是动态库,一般需要根据实际情况来确定。如果生成的库比较大,建议将其制作成动态库。
test1 目录中的 CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.15)
project(CALCTEST)
#将./目录下搜索到的源文件列表存储到SRC变量中
aux_source_directory(./ SRC)
#指定源文件所包含的头文件,HEAD_PATH是根目录下CMakeLists.txt下定义的头文件目录
include_directories(${HEAD_PATH})
# 包含静态库路径
link_directories(${LIB_PATH})
# 链接静态库
link_libraries(${CALC_LIB})
#设置可执行文件输出路径,在CMake中指定可执行程序输出的路径,
#也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,它的值还是通过set命令进行设置
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
# 生成可执行程序
add_executable(${APP_NAME_1} ${SRC})
include_directories
:指定头文件路径,HEAD_PATH
变量是在根节点文件中定义的
link_libraries
:指定可执行程序要链接的静态库
,CALC_LIB
变量是在根节点文件中定义的
set
:指定可执行程序生成的路径,EXEC_PATH
变量是在根节点文件中定义的
add_executable
:生成可执行程序,APP_NAME_1
变量是在根节点文件中定义的
此处的可执行程序链接的是静态库,最终静态库会被打包到可执行程序中,可执行程序启动之后,静态库也就随之被加载到内存中了。
test2 目录中的 CMakeLists.txt
文件内容如下:
cmake_minimum_required(VERSION 3.15)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
link_directories(${LIB_PATH})
add_executable(${APP_NAME_2} ${SRC})
target_link_libraries(${APP_NAME_2} ${SORT_LIB})
include_directories
:包含头文件路径,HEAD_PATH
变量是在根节点文件中定义的
set
:指定可执行程序生成的路径,EXEC_PATH
变量是在根节点文件中定义的
link_directories
:指定可执行程序要链接的动态库的路径,LIB_PATH
变量是在根节点文件中定义的
add_executable
:生成可执行程序,APP_NAME_2
变量是在根节点文件中定义的
target_link_libraries
:指定可执行程序要链接的动态库的名字
在生成可执行程序的时候,动态库不会被打包到可执行程序内部。当可执行程序启动之后动态库也不会被加载到内存,只有可执行程序调用了动态库中的函数的时候,动态库才会被加载到内存中,且多个进程可以共用内存中的同一个动态库,所以动态库又叫共享库。
一切准备就绪之后,开始构建项目,进入到根节点目录的build 目录
中,执行cmake 命令
,如下:
可以看到在build
目录中生成了一些文件和目录,如下所示:
通过上图可以得到如下信息:
在项目根目录的lib
目录中生成了静态库libcalc.a
在项目根目录的lib
目录中生成了动态库libsort.so
在项目根目录的bin
目录中生成了可执行程序test1
在项目根目录的bin
目录中生成了可执行程序test2
最后再来看一下上面提到的这些文件是否真的被生成到对应的目录中了:
tree bin/ lib/
bin/
├── test1
└── test2
lib/
├── libcalc.a
└── libsort.so
由此可见,项目构建完毕。
注意:
在项目中,如果将程序中的某个模块制作成了动态库或者静态库并且在CMakeLists.txt 中指定了库的输出目录,而后其它模块又需要加载这个生成的库文件,此时直接使用就可以了,如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来。
整个项目资源链接:
https://download.csdn.net/download/qq_44729609/90975012
注明:
此博客参考爱编程的大丙技术博客,原文链接 CMake 保姆级教程(上) | 爱编程的大丙 B站上有该博主的完整教程示例,可搜索参考