目录
1.Cmake简介
Cmake是一个跨平台的项目构建工具,能够自动化生成本地化的Makefile和工程文件,最后通过make编译即可,因此可以将Cmake看成是一款自动生成Makefile的工具。
图1-1 编译流程
图1-1中表示使用不同方式构建项目的流程:
- 蓝色虚线表示使用makefile构建项目的过程
- 红色实线表示使用cmake构建项目的过程
CMake的优点:
- 跨平台
- 能够管理大型项目
- 简化编译构建过程和编译过程
- 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能
2.Cmake指令的使用
2.1CMakeLists.txt
#指定使用cmake的最低版本
cmake_minimum_required(VERSION 3.0)
#指定工程的名称
project(CALC)
#生成可执行程序,基本语法为add_executable(可执行程序名 源文件名称)
add_executable(app add.c div.c main.c mult.c sub.c)
2.2cmake命令和make命令
若将CMakeLists、源文件、头文件放在同一目录下,其结构如下:
├── add.c
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
在当前路径下执行cmake命令后,目录中生成了一些新文件,如下所示:
├── add.c
├── CMakeCache.txt # new add file
├── CMakeFiles # new add dir
├── cmake_install.cmake # new add file
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile # new add file
├── mult.c
└── sub.c
可以看到新生成的文件中包含有一个makefile文件,若此时执行make命令,则可生成所需的可执行程序,如下所示:
├── add.c
├── app # 生成的可执行程序
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c
2.3Cmake项目的基本结构
从2.2节可以看到通过cmake 命令和make命令生成了makefile文件以及可执行程序,它们和CMakeLists.txt、源文件、头文件都放在了一起,如果项目较大就会显得过于混乱,因此为了便于管理和维护,将上述文件放入对应的目录下。
├── bin # 执行make命令后,生成的可执行程序的存放路径
│ ├── app # 可执行程序
├── build # cmake执行目录,存放执行cmake命令后生成的makefile等文件
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt # 注意C、M、L为大写,Lists的s不要漏写
├── include # 头文件目录
│ └── head.h # 头文件
└── src # 源文件目录
├── add.cpp # 源文件
├── div.cpp # 源文件
├── mult.cpp # 源文件
├── sub.cpp # 源文件
└── main.cpp # 用于测试的源文件
2.4set指令
在2.1节CMakeLists.txt中生成可执行文件时有5个源文件
add_executable(app add.c div.c main.c mult.c sub.c)
可通过set指令将这5个源文件赋予一个变量
# SET指令基本语法SET(VAR [VALUE]
# VAR:变量名
# VALUE:变量值
set(SRC_LIST add.c div.c main.c mult.c sub.c)
add_executable(app ${SRC_LIST})
2.5cmake中的宏定义
EXECUTABLE_OUTPUT_PATH:可执行程序存放位置,放在bin文件夹中
PROJECT_SOURCE_DIR:一般是工程的根目录
CMAKE_CURRENT_SOURCE_DIR:当前处理的CMakeLists.txt所在的路径
2.6搜索文件
2.6.1aux_source_directory命令
# 基本语法 aux_source_directory(< dir > < variable >)
# dir:要搜索的目录
# variable:从dir目录下搜索源文件列表存储到该变量中
# 搜索 src 目录下的源文件,存储到变量SRC_LIST中
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
# 生成可执行程序
add_executable(app ${SRC_LIST})
2.6.2file命令
# 基本语法file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
# 搜索头文件
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
# 搜索源文件
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
2.7包含头文件
为了在编译源文件的时候,能够找到与源文件对应的头文件,使得编译顺利通过,需要在CMakeLists.txt中设置包含头文件的目录。
# PROJECT_SOURCE_DIR对应的宏参照2.5节,一般为工程根目录
# include为头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)
2.8制作静态库或动态库
若我们编写的源文件不需要编译生成可执行程序,可以生成静态库或动态库供第三方使用,以下为制作这两种库文件的方法。
2.8.1制作静态库
# 基本语法add_library(库名称 STATIC 源文件1 [源文件2] ...)
# 将SRC_LIST变量中所表示的源文件生成静态库文件
# STATIC表示静态库,SHARED表示动态库
add_library(calc STATIC ${SRC_LIST})
在Linux中,静态库名字分为三部分:lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。
因此add_library(calc STATIC ${SRC_LIST})生成的对应的静态库文件为libcalc.a
2.8.2制作动态库
# 基本语法add_library(库名称 SHARED 源文件1 [源文件2] ...)
add_library(calc SHARED ${SRC_LIST})
在Linux中,动态库名字分为三部分:lib+库名字+.so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。
因此add_library(calc SHARED ${SRC_LIST})生成的对应的静态库文件为libcalc.so
2.8.3指定输出路径
使用LIBRARY_OUTPUT_PATH宏,指定库文件的生成路径
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库(在lib目录下)
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库(在lib目录下)
add_library(calc STATIC ${SRC_LIST})
2.9包含库文件
在编写程序过程中,会使用到库文件,下面对库文件的加载进行说明。
2.9.1链接静态库
若静态库不是系统提供的(自己制作或第三方提供的),可能会出现静态库找不到的情况,因此需指定静态库的路径。
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})
2.9.2链接动态库
# 基本语法
target_link_libraries( <target> )
# <target>为指定要加载的库文件的名字
# 该文件可能是一个源文件、静态库/动态库文件或可执行文件
动态库的链接和静态库是完全不同的:
- 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
- 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存
因此,在指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后,如下所示:
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字(此处为pthread)
target_link_libraries(app pthread)
3.嵌套的Cmake
通过Cmake管理大型项目时,若只使用一个CMakeLists.txt,将会使这个文件变得非常复杂,因此为便于管理和维护,为每一个源码目录都添加一个CMakeLists.txt。
3.1目录结构
├── build
├── calc
│ ├── add.cpp
│ ├── CMakeLists.txt
│ ├── div.cpp
│ ├── mult.cpp
│ └── sub.cpp
├── CMakeLists.txt
├── include
│ ├── calc.h
│ └── sort.h
├── sort
│ ├── CMakeLists.txt
│ ├── insert.cpp
│ └── select.cpp
├── test1
│ ├── calc.cpp
│ └── CMakeLists.txt
└── test2
├── CMakeLists.txt
└── sort.cpp
include目录 | 头文件目录 |
calc目录 | 包含计算相关的4个源文件,对应的头文件为include目录中的calc.h |
sort目录 | 包含排序相关的2个源文件,对应的头文件为include目录中的sort.h |
test1目录 | 测试目录,对计算相关的算法进行测试 |
test2目录 | 测试目录,对排序相关的算法进行测试 |
3.2节点关系
嵌套的 CMake 是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。
CMakeLists.txt 文件变量作用域的一些信息:
- 根节点CMakeLists.txt中的变量全局有效
- 父节点CMakeLists.txt中的变量可以在子节点中使用
- 子节点CMakeLists.txt中的变量只能在当前节点中使用
3.3添加子目录
建立父子节点之间的关系
# 基本语法add_subdirectory(source_dir)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
3.4问题分析及解决
将calc目录和sort目录中的源文件生成为库文件,供test1目录和test2目录中的测试文件使用。
3.4.1根目录
在根目录中的CMakeLists.txt文件中完成两件事:定义全局变量和添加子目录。具体如下:
cmake_minimum_required(VERSION 3.0)
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)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
3.4.2calc目录
calc 目录中的 CMakeLists.txt文件具体如下:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} STATIC ${SRC})
3.4.3sort目录
sort 目录中的 CMakeLists.txt文件具体如下:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
3.4.4test1目录
test1 目录中的 CMakeLists.txt文件具体如下:
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
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})
3.4.5test2目录
test2 目录中的 CMakeLists.txt文件具体如下:
cmake_minimum_required(VERSION 3.0)
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})
3.4.6构建项目
上述步骤完成后,开始构建项目,进入到根节点目录的build 目录中,执行cmake 命令,build目录中具体如下:
build
├── calc # 目录
├── CMakeCache.txt # 文件
├── CMakeFiles # 目录
├── cmake_install.cmake # 文件
├── Makefile # 文件
├── sort # 目录
├── test1 # 目录
└── test2 # 目录
在build目录下执行make命令
图中显示在lib目录下生成了静态库libcalc.a和动态库libsort.so,在bin目录下生成了可执行程序test1和test2。
bin/
├── test1
└── test2
lib/
├── libcalc.a
└── libsort.so
3.5完结撒花
4.参考内容链接
具体内容请参考如下链接: