目录
0.前言 & 准备
-
如果项目很大,或者项目中有很多的源码目录 ,在通过CMake管理项目的时候如果只使用一个
CMakeLists.txt
,那么这个文件相对会比较复杂 -
有一种化繁为简的方式就是给每个源码目录都添加一个
CMakeLists.txt
文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护 -
以下目录结构为例
shell. ├── CMakeLists.txt ├── bin ├── build ├── calc │ ├── CMakeLists.txt │ ├── add.c │ ├── div.c │ ├── mult.c │ └── sub.c ├── calc_test │ ├── CMakeLists.txt │ └── calc_main.c ├── include │ ├── calc.h │ └── sort.h ├── lib ├── sort │ ├── CMakeLists.txt │ └── sort.c └── sort_test ├── CMakeLists.txt └── sort_main.c
1.节点关系
- Linux的目录是树状结构,所以嵌套的CMake也是一个树状结构 ,最顶层的
CMakeLists.txt
是根节点,其次都是子节点 - 因此,需要了解一些关于
CMakeLists.txt
文件变量作用域 的一些信息:- 根节点
CMakeLists.txt
中的变量全局有效 - 父节点
CMakeLists.txt
中的变量可以在子节点中使用 - 子节点
CMakeLists.txt
中的变量只能在当前结点中使用
- 根节点
2.添加子目录
-
CMake中建立父子节点关系
cmakeadd_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
-
参数 :
source_dir
:指定了CMakeLists.txt
源文件和代码文件的位置,其实就是指定子目录binary_dir
:制定了输出文件的路径,一般不需要指定,忽略即可EXCLUDE_FROM_ALL
:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标
3.解决问题
1.根目录
-
根目录 中的
CMakeLists.txt
文件cmakecmake_minimum_required(VERSION 3.15) project(mult_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 calc_test) set(APP_NAME_2 sort_test) # 给当前结点添加子目录目录 add_subdirectory(calc) add_subdirectory(sort) add_subdirectory(sort_test) add_subdirectory(calc_test)
-
在根节点对应的文件中主要做了两件事情:定义全局变量和添加子目录
- 定义的全局变量主要是给子节点使用,目的是为了提高子节点中的CMakeLists.txt文件的可读性和可维护性,避免冗余并降低出错的概率
- 一共添加了四个子目录,每个子目录中都有一个
CMakeLists.txt
文件,这样它们的父子关系就被确定下来了
2.calc目录
-
calc目录 中的
CMakeLists.txt
文件cmakecmake_minimum_required(VERSION 3.15) project(calc) # 搜索源文件 aux_source_directory(./ SRC) include_directories(${HEAD_PATH}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_PATH}) add_library(${CALC_LIB} STATIC ${SRC})
3.sort目录
-
sort目录 中的
CMakeLists.txt
文件cmakecmake_minimum_required(VERSION 3.15) project(sort) # 搜索源文件 aux_source_directory(./ SRC) include_directories(${HEAD_PATH}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_PATH}) add_library(${SORT_LIB} STATIC ${SRC})
4.calc_test目录
-
calc_test目录 中的
CMakeLists.txt
文件cmakecmake_minimum_required(VERSION 3.15) project(calc_test) # 搜索源文件 aux_source_directory(./ SRC) include_directories(${HEAD_PATH}) link_directories(${LIB_PATH}) link_libraries(${CALC_LIB}) set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) add_executable(${APP_NAME_1} ${SRC})
5.sort_test
-
sort_test目录 中的
CMakeLists.txt
文件cmakecmake_minimum_required(VERSION 3.15) project(calc_test) # 搜索源文件 aux_source_directory(./ SRC) include_directories(${HEAD_PATH}) link_directories(${LIB_PATH}) link_libraries(${SORT_LIB}) set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) add_executable(${APP_NAME_2} ${SRC})
4.注意
-
在实际开发中,一个大型的 CMake 项目中,
project()
命令通常只在最外层的 CMakeLists.txt 文件中出现一次 -
顶层 CMakeLists.txt 文件是项目的入口点 ,在这里应该定义项目名称、全局设置和添加子目录
cmakecmake_minimum_required(VERSION 3.10) project(MyLargeProject) # 设置全局属性 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) # 添加子目录 add_subdirectory(src) add_subdirectory(lib)
-
在子目录的 CMakeLists.txt 文件中 ,通常不需要重复使用
project()
命令,相反,应该专注于定义目标(例如:库或可执行文件)、设置目标属性和包含路径cmake# 添加库文件 add_library(MyLibrary mylibrary.cpp) # 设置包含路径 target_include_directories(MyLibrary PUBLIC ${CMAKE_SOURCE_DIR}/include)