学习视频
CMake 保姆级教程【C/C++】_哔哩哔哩_bilibili
一、简介
CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有Makefile(通过 make 命令进行项目的构建),大多是IDE软件都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。
而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需make编译即可,所以可以把CMake看成一款自动生成 Makefile的工具,其编译流程如下图:
蓝色虚线表示使用makefile构建项目的过程
红色实线表示使用cmake构建项目的过程
优点:
跨平台
能够管理大型项目
简化编译构建过程和编译过程
可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能
二、CMake基本使用
注释
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(Calc)
# 定义工程会生成一个可执行程序 add_executable(可执行程序名 源文件名称)
add_executable(CalcAPP add.cpp div.cpp main.cpp mult.cpp sub.cpp)
windows 编辑一个简单的cmake eg1
main 文件:
#include <iostream>
using namespace std;
int main() {
cout<<"Hello World"<<endl;
return 0;
}
CMakeList.txt
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(HelloWorld)
# 定义工程会生成一个可执行程序 add_executable(可执行程序名 源文件名称)
add_executable(HelloWorldAPP main.cpp)
Cmake
打开vs
windows 编辑一个简单的cmake eg2
Cmakelist
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(Calc)
# 定义工程会生成一个可执行程序 add_executable(可执行程序名 源文件名称)
add_executable(CalcAPP add.cpp div.cpp main.cpp mult.cpp sub.cpp)
Set变量
# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
使用如下:
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c div.c main.c mult.c sub.c)
# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app ${SRC_LIST})
CMakeList 修改如下:
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(Calc)
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c div.c main.c mult.c sub.c)
# 方式2: 各个源文件之间使用分号 ; 间隔
set(CalcAPP_SRC_LIST add.cpp div.cpp main.cpp mult.cpp sub.cpp)
# 定义工程会生成一个可执行程序 add_executable(可执行程序名 源文件名称)
#add_executable(CalcAPP add.cpp div.cpp main.cpp mult.cpp sub.cpp)
# 使用set 将源文件转换为变量
add_executable(CalcAPP ${CalcAPP_SRC_LIST })
编译
Set 指定输出路径
在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,它的值还是通过set命令进行设置:
EXECUTABLE_OUTPUT_PATH 输出路径的宏
# 设置 输出路径
set(OUTPATH D:/workplace/Cplus/cc/bb6/x64)
set(EXECUTABLE_OUTPUT_PATH ${OUTPATH}/bin)
搜索路径1
使用aux_source_directory 命令可以查找某个路径下的所有源文件
aux_source_directory(< dir > < variable >)
解释:
dir:要搜索的目录
variable:将从dir目录下搜索到的源文件列表存储到该变量中
CMakeList改写:
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(CalcA)
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c div.c main.c mult.c sub.c)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src CalcAPP_SRC_LIST)
set(CMAKE_CXX_STANDARD 17)
# 方式2: 各个源文件之间使用分号 ; 间隔
#set(CalcAPP_SRC_LIST add.cpp div.cpp main.cpp mult.cpp sub.cpp)
# 设置 输出路径
#set(OUTPATH D:/workplace/Cplus/cc/bb6/x64)
#set(EXECUTABLE_OUTPUT_PATH ${OUTPATH}/bin)
文件夹也要修改:
重新编译:
搜索路径2
如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以,在CMake中为我们提供了搜索文件的命令,他就是file(当然,除了搜索以外通过 file 还可以做其他事情)
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
解释:
GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
eg:
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径。
file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
# 这是一个 CMakeLists.txt 文件,这里写的是cmake的最低版本 //单行注释
# 多行注释如下
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(CalcA)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB CalcAPP_SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
#include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
#aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src CalcAPP_SRC_LIST)
set(CMAKE_CXX_STANDARD 17)
# 使用set 将源文件转换为变量
add_executable(CalcAPP ${CalcAPP_SRC_LIST})
PROJECT_SOURCE_DIR 宏 一般是说当前项目的根目录
制作动态库
EXECUTABLE_OUTPUT_PATH 宏 设置了一个路径
目录结构:
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(CalcA)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB CalcAPP_SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
set(CMAKE_CXX_STANDARD 17)
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# SHARED 动态库
add_library(calc SHARED ${CalcAPP_SRC_LIST})
制作静态库
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(CalcA)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB CalcAPP_SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
set(CMAKE_CXX_STANDARD 17)
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# SHARED 动态库
#add_library(calc SHARED ${CalcAPP_SRC_LIST})
# 生成静态库
add_library(calc STATIC ${CalcAPP_SRC_LIST})
三、CMake链接库使用
1、链接静态库
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(CalcApp)
目录结构如下
cmakelist 编写
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(CalcA)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB CalcAPP_SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
set(CMAKE_CXX_STANDARD 17)
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库 我上一步中已经生产库的名称,要和文件的名称一样,不然会找不到
link_libraries(calc)
add_executable(Calcapplib ${CalcAPP_SRC_LIST})
在main函数中直接调用静态库
2、链接动态库
target_link_libraries(
<target>
该文件可能是一个源文件
该文件可能是一个动态库/静态库文件
该文件可能是一个可执行文件
PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。
3、动态库的链接和静态库是完全不同的:
静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存
生成动态库:
CMakeList
cmake_minimum_required (VERSION 3.0)
project(use_add_test)
add_executable(use_add_a main.cpp add.h)
# 动态链接库
target_link_libraries(use_add_a ${CMAKE_SOURCE_DIR}/dlllib/add_shared.lib)
综合项目:
生成静态库:
cmake_minimum_required(VERSION 3.0.0)
# 项目名称
project(test)
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/source)
aux_source_directory(${PROJECT_SOURCE_DIR}/source SRCS)
file(GLOB INC_PATH ${PROJECT_SOURCE_DIR}/include/*.h)
list(APPEND INCS ${INC_PATH})
#生成静态库 生产库的名字就是 test_static
add_library(test_static STATIC ${INCS} ${SRCS})
add_executable(test main.cpp)
#连接库的名字就是add_library 的库的名字(test_static)
target_link_libraries(test test_static)
编译
生产动态库
cmake_minimum_required(VERSION 3.0.0)
project(testdll)
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/source)
aux_source_directory(${PROJECT_SOURCE_DIR}/source SRCS)
file(GLOB INC_PATH ${PROJECT_SOURCE_DIR}/include/*.h)
list(APPEND INCS ${INC_PATH})
# 生成动态库
add_library(shared_dll SHARED ${INCS} ${SRCS})
# 测试引用动态库
add_executable(testdll main.cpp)
# 连接动态库
target_link_libraries(testdll shared_dll)
总结
target_link_libraries
link_libraries
建议在 CMake 项目中优先使用 target_link_libraries
日志
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
(无) :重要消息
STATUS :非重要消息
WARNING:CMake 警告, 会继续执行
AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
FATAL_ERROR:CMake 错误, 终止所有处理过程
案例:
# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")
宏定义
cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏 add_definitions(-D宏名称)
add_definitions(-DDEBUG)
add_executable(app ./main.cpp)
预定义宏
四、嵌套的CMake
目录结构
解释:
根节点CMakeList
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)
Calc节点CMakeList
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
#aux_source_directory 搜索当前目录下面的所有文件
aux_source_directory(./ SRC)
# 查找头文件的目录,这个头文件的目录是在父cmakelist 中定义的
include_directories(${HEAD_PATH})
# 设置生成库的名称 LIB_PATH 也是在父cmakelist 中定义的
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
# 生成静态库 静态库的名字 CALC_LIB 也是在父cmakelist 中定义的
add_library(${CALC_LIB} STATIC ${SRC})
Sort节点CMakeList
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
# 这个和Calc 中用的那个目录是一样的
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
test1节点CMakeList
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
# 查找头文件的目录,这个头文件的目录是在父cmakelist 中定义的
include_directories(${HEAD_PATH})
#指定可执行程序要链接的静态库,CALC_LIB变量是在根节点文件中定义的
link_directories(${LIB_PATH})
link_libraries(${CALC_LIB})
# 指定可执行程序生成的路径,EXEC_PATH变量是在根节点文件中定义的
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
# 生成可执行程序,APP_NAME_1变量是在根节点文件中定义的
add_executable(${APP_NAME_1} ${SRC})
test2节点CMakeList
cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
#包含头文件路径,HEAD_PATH变量是在根节点文件中定义的
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
# 指定可执行程序要链接的动态库的路径,LIB_PATH变量是在根节点文件中定义的
link_directories(${LIB_PATH})
add_executable(${APP_NAME_2} ${SRC})
#指定可执行程序要链接的动态库的名字
target_link_libraries(${APP_NAME_2} ${SORT_LIB})
五、CMake 连接OPencv 和PCL
CMake-OPencv
cmakelist
cmake_minimum_required(VERSION 3.0)
project(opencv_pcl_project)
# 搜索opencv 的相关库
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
if(CMAKE_VERSION VERSION_LESS "2.8.11")
include_directories(${OpenCV_INCLUDE_DIRS})
endif()
# 添加测试文件
add_executable(opencv_example example.cpp)
# 连接opencv 库文件
target_link_libraries(opencv_example PRIVATE ${OpenCV_LIBS})
#include "opencv2/opencv.hpp"
#include <iostream>
int main()
{
std::cout<<"CMake构建opencv示例:"<<std::endl;
std::cout<< cv::getBuildInformation() <<std::endl;
return 0;
}
CMake-PCL
CMake集成OpenCV、CGAL和PCL的项目配置与编译指南-优快云博客