CMake 基本使用

CMake 保姆级教程(上) | 爱编程的大丙

CMake 保姆级教程(下) | 爱编程的大丙

学习视频

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的项目配置与编译指南-优快云博客

六、流程控制

比较

文件操作 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值