CMake入门

前言

我们在很多SLAM 的源码里都能看到CMake 的使用,这个CMake 到底是什么呢有什么用呢?

一、cmake的简介

CMake ( Cross platform Make ) 是一个开源的跨平台自动化建构系统,用来管理程序构建,不依赖于特定编译器。CMake 可以自动化编译源代码、创建库、生成可执行二进制文件等,为开发者节省了大量的时间,可以说是工程实践的必备工具。

CMake 通过使用简单的配置文件 CMakeLists.txt,自动生成不同平台的构建文件(如 Makefile、Ninja 构建文件、Visual Studio 工程文件等),简化了项目的编译和构建过程。CMake 本身不是构建工具,而是生成构建系统的工具,它生成的构建系统可以使用不同的编译器和工具链。

1.CMake 的优点

1.开源。

2.跨平台使用,根据目标用户的平台进一步生成所需的本地化Makefile 和工程文件,如UNIX 的Makefile 或Windows 的Visual Studio 工程。

3.可管理大型项目,如OpenCV 、Caffe 和MySQL Server。

4. 自动化构建编译,构建项目效率非常高。

5.CMake 支持多种语言,如C 、C ++ 和Java 等。

2.使用CMake 的注意事项

1.需要根据CMake 专用语言和语法自己编写CMakeLists. txt 文件(注意这里的大小写)。

2. 如果项目已经有非常完备的工程管理工具,并且不存在维护问题,则没有必要使用CMake 。

3.构建配置 

CMakeLists.txt 文件: CMake 的配置文件,用于定义项目的构建规则、依赖关系、编译选项等。每个 CMake 项目通常包含一个或多个 CMakeLists.txt 文件。

构建目录: 为了保持源代码的整洁,CMake 鼓励使用独立的构建目录(Out-of-source 构建)。这样,构建生成的文件与源代码分开存放。

4.基本工作流程

1.编写 CMakeLists.txt 文件: 定义项目的构建规则和依赖关系。

2.生成构建文件: 使用 CMake 生成适合当前平台的构建系统文件(例如 Makefile、Visual Studio 工程文件)。

3.执行构建: 使用生成的构建系统文件(如 makeninjamsbuild)来编译项目。

二、cmake的安装

CMake 的安装方法很简单。这里分别介绍CMake 在Windows 系统和Linux 系统下的安装方法:

cmake下载官网:

Download CMake

我的是.16版本,截至目前已经更新到.31了。

下载时需要梯子才不会出现卡顿,如果下载不了我已将资源进行了绑定请直接下载就行。 

1.windows下安装按照提示无脑进行安装

2.linux(ubuntu)

学习slam这里就默认你是ubuntu系统

sudo apt-get install cmake

如果想在Linux 系统下使用图形化界面,则用如下代码:

sudo apt-get install cmake—gui

但是ubuntu源里的版本可能比较低。

利用下面的命令检查你的cmake版本

cmake --version

3.通过源码编译安装

访问 CMake 官方网站下载源码包。

解压源码包,进入解压后的目录。

执行以下命令编译和安装:

./bootstrap
make
sudo make install

三、cmake自动化构建项目的魅力

用CMake 构建项目非常方便,以计算机视觉领域最常用的开源库OpenCV 为例,展示一下CMake 的魅力。早期我在Windows 系统下学习OpenCV 时,每次配置环境都非常头疼,准备工作烦琐、问题明显。

1.准备工作。

1.手动添加环境变量。

2.在项目中手动添加包含路径。

3.在项目中手动添加库路径。

4.在项目中手动添加链接库名。

5.在Debug 和Release下分别配置对应的库。

2.存在的问题

1.方法不通用,对于不同的OpenCV 版本,库的名称也不一样,在手动添加时需要修改库名称。

2.构建好的项目不能直接移植到其他平台上, 需要重新配置,代码的移植成本很高。

3.整个过程非常烦琐,并且非常容易出错。

但是自从我开始使用CMake自动构建项目,以上烦恼都消失啦!使用CMake 只需要简单几步,即可自动化完成项目构建。

CMake 一般有两种使用方式, 一种是命令行方式, 一般在Linux系统下使用比较多; 另一种是图形化界面, 一般在Windows 系统下比较常用。

我们这里以ubuntu系统为例。还以编译OpenCV 为例,假设我们已经提前下载好了OpenCV 的某个版本(这里用的是OpenCV3.4.6) 的源码,解压后的文件夹名字为opencv-3.4.6。如果用命令行来构建工程, 则先在该文件夹同级目录下打开一个终端,执行如下命令即可成功编译。

//在Ubuntu系统终端里编译OpenCV,假设解压后的义件夹名字为opencv-3.4.6
cd opencv-3.4.6 //进人opencv-3.4.6义件夹内
mkdir build //新建build 文件夹
cd build //进人build 文件夹内
cmake .. //编译上层目录的CMakeLists.txt 文件,生成Makefile 等文件
make //调用编译器编译源文件
sudo make install //将编译后的文件安装到系统中

为什么要新建一个build 文件夹呢?

新建build 文件夹是为了存放使用cmake 命令生成的中间文件,这些中间文件是在编译时产生的临时文件,在发布代码时并不需要将它们一起发布,最好删除掉。如果不新建build 文件夹,那么这些中间文件会混在代码文件中, 一个一个手动删除会非常麻烦。build 是大家常用的文件夹名, 当然,你也可以改成任意名字。

cmake 命令后面的两个点是什么意思呢?

一个点(.)代表当前目录, 两个点(..) 代表上一级目录。因为CMakeLists. txt 和build 文件夹位于同一层级目录,在进入build文件夹后, CMakeLists.txt 相对于当前位置在上一级目录中,所以在使用cmake命令的时候需要用两个点,否则会报错,提示找不到Makefile 文件。

cmake图形化界面这里暂时用不到所以先不学习。

CMakeLists. txt 文件,其内部巳经帮我们自动化地处理好了工程文件之间的复杂依赖关系。对于现成的第三方库或别人编写好的项目, CMakeLists.txt 文件都已经编写好了,我们只需要像前面那样简单操作就能自动构建好工程。但是,如果我们要自己搭建一个项目,就需要自己编写CMakeLists. txt 文件了,这需要一定的学习时间。下面我们来学习编写CMakeLists.txt 文件时常用的指令。

四、CMake常用指令

CMake 中有很多指令,我们可以在官网上查询到每个指令的介绍。

cmake指令查找工具

为方便学习,这里介绍几个常用的、比较重要的指令。

1、指定 CMake 的最低版本要求
cmake_minimum_required(VERSION <version>)

例如:

#指定要求最小的CMake 版本,如果版本小于该要求,则程序终止
cmake_mininum_required(VERSION 2.8)
2、定义项目的名称和使用的编程语言
project(<project_name> [<language>...])

使用的编程语言可以省略。

例如:

#设置当前项目名称为test
project(test)
3.用指定的源文件为工程添加可执行文件
add_executable(<target> <source_files>...)

 例如:

add_executable(MyExecutable main.cpp other_file.cpp)
4、创建一个库(静态库或动态库)及其源文件
add_library(<target> <source_files>...)

例如:

add_library(MyLibrary STATIC library.cpp)

静态库(STATIC)适合独立运行的应用,无需额外的 .so.dll 文件。 

动态库(SHARED)适合大型项目插件系统,多个程序可共享库,更新方便。

5. 链接目标文件与其他库
target_link_libraries(<target> <libraries>...)

例如:

target_link_libraries(MyExecutable MyLibrary)
6. 添加头文件搜索路径
include_directories(<dirs>...)

例如:

#指定头文件的搜索路径,方便编译器查找相应头文件
include_directories
#例子:文件main.cpp中使用到路径/usr/local/include/opencv/cv.h中的这个文件
#那么需要在CMakeLists.txt中添加include_directories{"/usr/local/include"}
#这样使用时在main.cpp 开头写上#include"opencv/cv.h",编译器即可自动搜索到该头文件
7. 设置变量的值
set(<variable> <value>...)

例如:

#例子: set (SRC_LST main.cpp other.cpp) 表示定义SRC_LST代替后面的两个.cpp文件
8.查找库和包 

CMake 可以通过 find_package() 指令自动检测和配置外部库和包。

常用于查找系统安装的库或第三方库。

#搜索第三方库
find_package(packageName version EXACT/QUIET/REQUIRED)
# version: 指定查找库的版本号。EXACT: 要求该版本号必须精确匹配。QUIET: 禁止显示没有找到时的
#警告信息
# REQUIRED 选项表示如果包没有找到,则CMake 的过程会终止,并输出警告信息
#当find_package 找到一个库时,以下变量会自动初始化, NAME 表示库的名称
# <NAME>_FOUND : 显示是否找到库的标记
# <NAME>_INCLUDE_DIRS 或 <NAME>_INCLUDES:头文件路径
# <NAME>_LIBRARIES 或<NAME>_LIBS: 库文件
9.打印输出信息
#打印输出信息
message(mode "message text")
#mode包括FATAL ERROR 、WARNING 、STATUS 、DEBUG 等,双引号内是打印的内容
message(FATAL_ERROR "This is a fatal error!")   # 立即终止 CMake
message(WARNING "This is a warning!")          # 显示警告信息
message(STATUS "Project is being configured...") # 常规状态信息
message(DEBUG "Debug info: Var = ${MY_VAR}")  # 仅调试模式下显示
10.变量和缓存 

CMake 使用变量来存储和传递信息,这些变量可以在 CMakeLists.txt 文件中定义和使用。

变量可以分为普通变量和缓存变量。

变量定义与使用

定义变量

set(MY_VAR "Hello World")

使用变量

message(STATUS "Variable MY_VAR is ${MY_VAR}")

缓存变量

缓存变量存储在 CMake 的缓存文件中,用户可以在 CMake 配置时修改这些值。缓存变量通常用于用户输入的设置,例如编译选项和路径。

定义缓存变量

set(MY_CACHE_VAR "DefaultValue" CACHE STRING "A cache variable")

使用缓存变量

message(STATUS "Cache variable MY_CACHE_VAR is ${MY_CACHE_VAR}")
11.其他的一些指令 

#向当前工程中添加文件的子目录,目录可以是绝对路径或相对路径
add_subdirectory(source_dir)
#在目求下查找所有源义件
aux_source_directory(dir varname)


#列表操作(读、搜索、修改、排片)
list
#追加例子: LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR)/cmake_modules)
#判断语句,使用方法和C语言一致
if, elseif, endif
#循环指令,使用方法类似c语言中的for 循环
foreach , endforeach

CMake中的一些常用的、预定义的变量如下:

PROJECT_NAME: 工程名称,替代project(name) 中的name
PROJECT_SOURCE_DIR: 工程路径,通常是包含project 指令的CMakeLists.txt 文件所在的路径
EXECUTABLE_OUTPUT_PATH : 可执行文件输出路径
LIBRARY_OUTPUT_PATH: 库文件输出路径
CMAKE_BINARY_DIR : 默认是build 文件夹所在的绝对路径
CMAKE_SOURCE_DIR : 源文件所在的绝对路径

 一个重要的指令:find _package

如果我们当前待编译的工程需要使用第三方库, 则需要知道3 件事,即第三方库的名称、去哪里找库的头文件、去哪里找库文件。要解决这些问题,就可以使用find _package 指令。比如需要一个第三方库Pangolin, 如果不使用find _package 指令, 则需要根据库的安装路径在CMakeLists.txt 中指定头文件和库文件的路径。像这样:

#在不使用find_package 指令的情况下需要手动指定路径
#下面的yourpathl 需要替换为Pangolin 头文件在当前计算机上的路径
include_directiories(yourpathl/Pangolin)
#下面的yourpath2 需要替换为生成的库文件在当前计算机上的路径
target_link_libraries (mydemo yourpath2/Pangolin.so)

 而如果使用find _package 指令, 则在CMakeLists.txt中这样写:

#在使用find_package 指令的情况下自动指定路径
#查找计算机中已经安装的Pangolin 库
find_package(Pangolin REQUIRED)
#自动将找到的Pangolin库中头文件的路径添加到工程中
include_directories(${Pangolin_INCLUDE_DIRS})
#自动将找到的Pangolin 库文件链接到工程中
target_link_libraries (mydemo ${Pangolin_LIBRARIES})

单从代码来看,第二种方式确实多了一行代码,但是实际上比第一种方式有极大的灵活性和自动化性, 主要体现在以下方面:

1.不需要手动修改每个库文件的实际路径。每个人的计箕机环境不同, 库安装路径也不同,如果使用第一种方式,那么当项目给其他人用时, 每个使用者都需要手动修改每个库的位置,不仅烦琐,还容易出错。

2.格式化表达头文件和库文件名。这样我们在构建CMakeLists.txt 文件时会非常方便,尤其是库文件互相依赖或需要同时编译多个可执行文件时,按照格式化的方法来写头文件和库文件名即可,从而可以在不同平台和环境下维护同一个CMakeLists.txt 文件。

正是有了以上优势,我们在使用第三方库时基本不需要改动作者写好的第三方库里的CMakeLists.txt 文件,直接编译即可。

五、CMake 使用注意事项

1.CMake 指令不区分大小写

CMake指令可以全用大写或全用小写, 甚至大小写混用也可以,自己统一风格即可。比如下面两个指令表示的意义相同。其中,以#开头的行表示注释。

#指令不区分大小写
#指令add_executable 可以用小写字母表示
add_executable (hello main. cpp)
#指令ADD_EXECUTABLE 也可以用大写字母表示
ADD_ EXECUTABLE (hello main.cpp)

2. 参数和变量名称

参数和变量名称只能用字母、数字、下画线、破折号中的一个或多个组合,并且严格区分大小写。引用变址的形式为${} 。如果有多个参数, 则中间应该使用
空格间隔, 示例如下。

#参数和变量名称严格区分大小写
#将OpenCV 库和 Sophus 库一起命名为THIRD_PARTY_LIBS
#${OpenCV_LIBS}表示引用Opencv的所有的库
#注意${OpenCV_LIBS}和${Sophus_LIBRARIES}之间需要用空格间隔
set (THIRD_PARTY LIBS ${OpenCV _LIBS} ${Sophus_LIBRARIES})
#添加可执行文件名称为test_Demo
add_executable(test_Demo test.cpp)
#为可执行文件添加链接库,注意这里的test_Demo 必须和上面的大小写一致
#${THIRD_PARTY_LIBS}表示引用前面定义的变量THIRD_PARTY_LIBS
target_link_libraries(test_Demo ${THIRD_PARTY_LIBS})

一般来说,我们的工程是存在多个目录的。使用CMakeLists.txt 构建工程有两种方法。

第一种:工程存在多个目录,只用一个CMakeLists. txt 文件来管理。典型的结构如下:

// include 文件夹
include
    incl.h
    inc2.h
// source 文件夹
source
    srcl.cpp
    src2.cpp

// app 为主函数文件夹
app
    main.cpp
// CMakeLists.txt 和include 、source 及app 位于同级目录下
CMakeLists.txt

 一个典型的案例就是ORB-SLAM2 代码, 它只在最外层使用了一个CMakeLists.txt来构建整个工程。我们来看看它是如何链接不同目录下的文件的。

#指定了该项目最低要求的 CMake 版本是 2.8。
cmake_minimum_required(VERSION 2.8)
#指定了项目的名称,项目名为 ORB_SLAM2。
project(ORB_SLAM2)

#这部分检查是否已设置 CMake 的构建类型 (CMAKE_BUILD_TYPE)。
#如果没有指定,默认设置构建类型为Release。
#常见的构建类型
#Release:用于发布的优化版本,通常启用优化选项。
#Debug:用于调试的版本,包含调试符号和调试信息,关闭优化。
#RelWithDebInfo:包含调试信息的优化版本。
#MinSizeRel:优化并尽量减小代码尺寸的版本。
IF(NOT CMAKE_BUILD_TYPE)
  SET(CMAKE_BUILD_TYPE Release)
ENDIF()

MESSAGE("Build type: " ${CMAKE_BUILD_TYPE})

#设置C编译器的编译选项。
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall  -O3 -march=native ")
#设置C++编译器的编译选项。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -O3 -march=native")

# Check C++11 or C++0x support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
   add_definitions(-DCOMPILEDWITHC11)
   message(STATUS "Using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
   add_definitions(-DCOMPILEDWITHC0X)
   message(STATUS "Using flag -std=c++0x.")
else()
   message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

#查找 OpenCV 3.0 及以上版本。如果找不到 OpenCV 3.0,QUIET 参数表示不会输出错误信息,
#而是继续查找其他版本。
find_package(OpenCV 3.0 QUIET)
if(NOT OpenCV_FOUND)
   find_package(OpenCV 2.4.3 QUIET)
   if(NOT OpenCV_FOUND)
      message(FATAL_ERROR "OpenCV > 2.4.3 not found.")
   endif()
endif()

find_package(Eigen3 3.1.0 REQUIRED)
find_package(Pangolin REQUIRED)

include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/include
${EIGEN3_INCLUDE_DIR}
${Pangolin_INCLUDE_DIRS}
)

#设置CMake输出共享库文件的目录,这里指定为项目的lib目录。
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)

add_library(${PROJECT_NAME} SHARED
src/System.cc
src/Tracking.cc
src/LocalMapping.cc
src/LoopClosing.cc
src/ORBextractor.cc
src/ORBmatcher.cc
src/FrameDrawer.cc
src/Converter.cc
src/MapPoint.cc
src/KeyFrame.cc
src/Map.cc
src/MapDrawer.cc
src/Optimizer.cc
src/PnPsolver.cc
src/Frame.cc
src/KeyFrameDatabase.cc
src/Sim3Solver.cc
src/Initializer.cc
src/Viewer.cc
)

target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBS}
${EIGEN3_LIBS}
${Pangolin_LIBRARIES}
${PROJECT_SOURCE_DIR}/Thirdparty/DBoW2/lib/libDBoW2.so
${PROJECT_SOURCE_DIR}/Thirdparty/g2o/lib/libg2o.so
)

# Build examples

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/RGB-D)

add_executable(rgbd_tum
Examples/RGB-D/rgbd_tum.cc)
target_link_libraries(rgbd_tum ${PROJECT_NAME})

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Stereo)

add_executable(stereo_kitti
Examples/Stereo/stereo_kitti.cc)
target_link_libraries(stereo_kitti ${PROJECT_NAME})

add_executable(stereo_euroc
Examples/Stereo/stereo_euroc.cc)
target_link_libraries(stereo_euroc ${PROJECT_NAME})


set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Monocular)

add_executable(mono_tum
Examples/Monocular/mono_tum.cc)
target_link_libraries(mono_tum ${PROJECT_NAME})

add_executable(mono_kitti
Examples/Monocular/mono_kitti.cc)
target_link_libraries(mono_kitti ${PROJECT_NAME})

add_executable(mono_euroc
Examples/Monocular/mono_euroc.cc)
target_link_libraries(mono_euroc ${PROJECT_NAME})

第二种: 工程存在多个目录, 每个源文件目录都使用一个CMakeLists.txt文件来管理。典型的结构如下:

// include 文件夹
include
    incl.h
    inc2.h
// source 文件夹下除了源文件,还有CMakeLists.txt 文件
source
    srcl.cpp
    src2.cpp
    CMakeLists.txt
// app 为主函数文件夹,其下除了源文件,还有CMakeLists.txt 文件
app
    main.cpp
    CMakeLists.txt
// CMakeLists.txt和include 、source 及app 位于同级目录F
CMakeLists.txt

 一个典型的案例就是《视觉SLAM 十四讲:从理论到实践》里的源代码。因为两种方法本质上没有什么不同,选择其中一种即可,故这里不做过多赘述。

结束语

以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。

CMake是一个跨平台的开源构建系统,它帮助开发者在多种操作系统上自动化软件项目的构建过程。以下是CMake入门的一些步骤和基本概念: 1. **安装CMake**:首先,你需要从CMake官网下载并安装适用于你的系统的版本。对于Windows、Mac OS和Linux都有预打包的二进制包可供选择。 2. **创建CMakeLists.txt**:这是CMake的核心,它是描述你的项目结构和依赖关系的文本文件。通常放在项目根目录下。 3. **配置生成器**:CMake支持多种构建系统,如Unix Makefiles、Visual Studio等。通过命令行指定`cmake -G`选项,告诉CMake你想要哪种生成器。 4. **生成构建文件**:运行`cmake`命令初始化CMake流程,生成对应的构建文件(如Makefile或Visual Studio解决方案文件)。 5. **构建项目**:根据选定的构建系统(如make或msbuild),运行`make`或`cmake --build .`命令来实际构建你的程序。 6. **添加变量和指令**:CMake提供了许多变量和函数,如设置源代码路径、添加库依赖、设置编译选项等,可以灵活调整构建配置。 7. **了解基本语法**:学习CMake的基本语法,如IF()条件语句、ADD_EXECUTABLE()添加可执行文件、INCLUDE()导入外部CMakeLists.txt等。 8. **实践项目**:通过参与一些小型项目或者使用官方的示例来熟悉CMake的工作流程。 **相关问题--** 1. CMake有哪些常见的错误及其解决办法是什么? 2. 如何在CMakeLists.txt中处理不同平台的差异? 3. 怎样在已经存在的CMake项目中添加新的模块或依赖?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值