目录
CMake详细教程
1 CMake简介
CMake 是"Cross platform MAke"的缩写,CMake是开源、跨平台的构建工具,可以让我们通过编写简单的配置文件去生成本地的Makefile,这个配置文件是独立于运行平台和编译器的,这样就不用亲自去编写Makefile了,而且配置文件可以直接拿到其它平台上使用,无需修改,非常方便。
2 CMake特点
- Cmake 支持很多语言: C、C++、Java 等
- 跨平台使用,根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix的Makefile 或Windows 的 Visual Studio 工程
- 能够管理大型项目,比如OpenCV、Caffe、MySql Server
- 自动化构建编译,CMake 构建项目效率非常高
- 需要根据CMake 专用语言和语法来自己编写CMakeLists.txt 文件
- 如项目已经有非常完备的工程管理工具,并且不存在维护问题,没必要迁移到CMake
3 CMake安装
Windows下:Windows下Cmake安装步骤详解(图文)_cmake安装windows_L_Li_L的博客-CSDN博客
Linux下:Linux安装CMake_氷泠的博客-CSDN博客
4 CMake使用注意事项
- CMake 构建专用定义文件,文件名严格区分大小写
- 工程存在多个目录,可以每个目录都放一个CMakeLists.txt文件
- 工程存在多个目录,也可以只用一个CMakeLists.txt文件管理
- 不区分大小写,可以全用大写,全用小写,甚至大小写混合,自己统一风格即可
- 严格大小写相关。名称中只能用字母、数字、下划线、破折号
- 用${}来引用变量
- 参数之间使用空格进行间隔
5 CMake常用指令介绍
5.1 CMake官方指令
Index — CMake 3.26.0-rc6 Documentation
5.2 CMake常用指令
以高翔《SLAM十四讲ch13》的案例为例。
cmake_minimum_required(VERSION 2.8)
project(myslam)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O3 -fopenmp -pthread")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
############### dependencies ######################
# Eigen
include_directories("/usr/include/eigen3")
# OpenCV
find_package(OpenCV 3.1 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# pangolin
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})
# Sophus
find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})
# G2O
find_package(G2O REQUIRED)
include_directories(${G2O_INCLUDE_DIRS})
# glog
find_package(Glog REQUIRED)
include_directories(${GLOG_INCLUDE_DIRS})
# gtest
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# gflags
find_package(GFlags REQUIRED)
include_directories(${GFLAGS_INCLUDE_DIRS})
# csparse
find_package(CSparse REQUIRED)
include_directories(${CSPARSE_INCLUDE_DIR})
set(THIRD_PARTY_LIBS
${OpenCV_LIBS}
${Sophus_LIBRARIES}
${Pangolin_LIBRARIES} GL GLU GLEW glut
g2o_core g2o_stuff g2o_types_sba g2o_solver_csparse g2o_csparse_extension
${GTEST_BOTH_LIBRARIES}
${GLOG_LIBRARIES}
${GFLAGS_LIBRARIES}
pthread
${CSPARSE_LIBRARY}
)
enable_testing()
############### source and test ######################
include_directories(${PROJECT_SOURCE_DIR}/include)
add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(app)
(1)指定要求最小的cmake版本,如果版本小于该要求,程序终止
cmake_minimum_required()
(2)设置当前项目名称
project(name)
(3)设置模式
CMAKE_BUILD_TYPE
# Debug: 调试模式,输出调试信息,不做优化
# Release: 发布模式,没有调试信息,全优化
# RelWithDebInfo::类似Release, 但包括调试信息
# MinSizeRel: 一种特殊的Release模式,会特别优化库的大小
(4)指定头文件的搜索路径
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
- 指定头文件的搜索路径,编译器查找相应头文件
- 举例:include_directories("/usr/include/eigen3")查找eigen3库的路径
- CMakeLists.txt 中添加 include_directories(/usr/local/include)
(5)set用于给下面的变量设置值
参考文章:Cmake入门之——Set方法(六)_cmake set cache string_PGzxc的博客-CSDN博客
- 一般变量(Set Normal Variable)
set(<variable> <value>... [PARENT_SCOPE])
# variable:只能有一个
# value:可以有0个,1个或多个,当value值为空时,方法同unset,用于取消设置的值
# PARENT_SCOPE(父作用域):作用域,除PARENT_SCOPE外还有function scope(方法作用域)和directory scope(目录作用域)
- 缓存变量(Set Cache Entry)
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
# variable:只能有一个
# value:可以有0个,1个或多个,当value值为空时,方法同unset,用于取消设置的值
# CACHE:关键字,说明是缓存变量设置
## type(类型):必须为以下中的一种:
## BOOL:有ON/OFF,两种取值
## FILEPATH:文件的全路径
## PATH:目录路径
## STRING:字符串
## INTERNAL:字符串
## docstring:总结性文字(字符串)
# [FORCE]:变量名相同,第二次调用set方法时,第一次的value将会被覆盖
- 环境变量(Set Environment Variable)
set(ENV{<variable>} [<value>])
# variable:只能有一个
# value:一般来说,只有一个,为空时,将清除之前设置的变量值,多个时,取值最近的一个,之后的值将被忽略
(6)添加可执行文件
add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1] [source2 ...])
- 用指定的源文件为工程添加可执行文件
- name:生成可执行文件的名字,必须在工程内全局唯一
- WIN32:有此参数时,WIN32_EXECUTABLE属性会被置为true,此时在windows环境下创建的可执行文件将以WinMain函数代替main函数作为程序入口,构建而成的可执行文件为GUI应用程序而不是控制台应用程序
- MACOSX_BUNDLE: 有此参数时,MACOSX_BUNDLE属性会被置为true,此时在macOS或者iOS上构建可执行文件目标时,目标会成为一个从Finder启动的GUI可执行程序
- EXCLUDE_FROM_ALL:有此参数时,此目标就会被排除在all target列表之外,即在执行默认的make时,不会构造此目标,需要构造此目标的时候,需要手动构建,如:
(7)生成链接库文件
add_library(libname STATIC/SHARED sources)
- 将指定的源文件生成链接库文件。STATIC 为静态链接库,SHARED 为共享链接库
(8)添加库链接
target_link_libraries (target library1 library2 ...)
- 为库或二进制可执行文件添加库链接,要用在add_executable之后。
(9)源文件向项目(project)添加可执行文件
add_executable(<name> IMPORTED [GLOBAL])
# Debug: 调试模式,输出调试信息,不做优化
# Release: 发布模式,没有调试信息,全优化
# RelWithDebInfo::类似Release, 但包括调试信息
# MinSizeRel: 一种特殊的Release模式,会特别优化库的大小
-
name
:导入可执行文件目标的名字 IMPORTED
:导入的目标文件需指定IMPORTED
属性,IMPORTED
属性指定后,目标文件的属性IMPORTED
被置为true
,在工程内构建生成的可执行文件的IMPORTED
属性会被置为false
。
(10)指明当前的执行程序依赖的动态库
add_dependencies (target-name depend)
- 为上层target添加依赖,一般不用
- 若只有一个targets有依赖关系,一般选择使用 target_link_libraries
- 如果两个targets有依赖关系,并且依赖库也是通过编译源码产生的。这时候用该指令可以在编译
(11)向当前工程添加存放源文件的子目录
add_subdirectory(source_dir)
- 向当前工程添加存放源文件的子目录,目录可以是绝对路径或相对路径
(12)在目录下查找所有源文件
aux_source_directory( dir varname)
(13)打印输出信息
message(mode "message text" )
- 打印输出信息,mode包括FATAL_ERROR、WARNING、STATUS、DEBUG等
- message(STATUS “Set debug mode")
PROJECT_NAME:项目名称,与project( xxx) 一致
PROJECT_SOURCE_DIR:即内含 project() 指令的 CMakeLists 所在的文件夹
EXECUTABLE_OUTPUT_PATH:可执行文件输出路径
LIBRARY_OUTPUT_PATH :库文件输出路径
CMAKE_BINARY_DIR:默认是build文件夹所在的绝对路径
CMAKE_SOURCE_DIR:源文件所在的绝对路径
(14)搜索外部库
find_package(package version EXACT/QUIET/REQUIRED)
- 功能:采用两种模式( FindXXX.cmake和XXXConfig.cmake )搜索外部库
- 示例:find_package( OpenCV 3.4 REQUIRED )
- version:指定查找库的版本号。EXACT:要求该版本号必须精确匹配。QUIET:禁掉没有找到时的警告信息。REQUIRED选项表示如果包没有找到的话,CMake的过程会终止,并输出警告信息。
搜索有两种模式
- Module模式:搜索CMAKE_MODULE_PATH指定路径下的FindXXX.cmake文件,执行该文件从而找到XXX库。其中,具体查找库并给XXX_INCLUDE_DIRS和XXX_LIBRARIES两个变量赋值的操作由FindXXX.cmake模块完成。
- Config模式:搜索XXX_DIR指定路径下的XXXConfig.cmake文件从而找到XXX库。其中具体查找库并给XXX_INCLUDE_DIRS和XXX_LIBRARIES两个变量赋值的操作由XXXConfig.cmake模块完成。
两种模式看起来似乎差不多,不过cmake默认采取Module模式,如果Module模式未找到库,才会采取Config模式。
如果XXX_DIR路径下找不到XXXConfig.cmake文件,则会找/usr/local/lib/cmake/XXX/中的XXXConfig.cmake文件。总之,Config模式是一个备选策略。通常,库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
若XXX安装时没有安装到系统目录,则无法自动找到XXXConfig.cmake,需要在CMakeLists.txt最前面添加XXX的搜索路径。
(15)列表操作
LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
list(LENGTH <list><output variable>)
list(GET <list> <elementindex> [<element index> ...]<output variable>)
list(APPEND <list><element> [<element> ...])
list(FIND <list> <value><output variable>)
list(INSERT <list><element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value>[<value> ...])
list(REMOVE_AT <list><index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
- ENGTH:回list的长度
- GET:返回list中index的element到value中
- APPEND:添加新element到list中
- FIND:返回list中element的index,没有找到返回-1
- INSERT:将新element插入到list中index的位置
- REMOVE_ITEM:从list中删除某个element
- REMOVE_AT:从list中删除指定index的element
- REMOVE_DUPLICATES:从list中删除重复的element
- REVERSE:将list的内容反转
- SORT:将list按字母顺序排序
5.3 静态库和共享库
(1)静态库
原理:在编译时将源代码复制到程序中,运行时不用库文件依旧可以运行。
优点:运行已有代码,运行时不用再用库;无需加载库,运行更快
缺点:占用更多的空间和磁盘;静态库升级,需要重新编译程序
(2)共享库(常用)
原理:编译时仅仅是记录用哪一个库里面的哪一个符号,不复制相关代码
优点:不复制代码,占用空间小;多个程序可以同时调用一个库;升级方便,无需重新编译
缺点:程序运行需要加载库,耗费一定时间
6 ORB_SLAM2中Cmake文件解析
cmake_minimum_required(VERSION 2.8) #设定cmake最小版本号
project(ORB_SLAM2) #指定项目工程
#IF 和 ENDIF构成一个if语句
IF(NOT CMAKE_BUILD_TYPE) #编译的类型(debug;release)
SET(CMAKE_BUILD_TYPE Release)
ENDIF()
MESSAGE("Build type: " ${CMAKE_BUILD_TYPE}) #输出消息:"Build type: Release"(打印调试信息)
#cmake_c_flags用来设置编译选项 如 -g -wall(不展示警告);-march=native,GCC会自动检测你的CPU支持的指令集
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -march=native ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -march=native")
# Check C++11 or C++0x support
include(CheckCXXCompilerFlag) #include:从文件或模块加载并运行CMake代码 ;CheckCXXCompilerFlag: 检查CXX编译器是否支持给定标志
#以下代码都用于自动判断系统编译器是否支持c++11标准;
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)
#list(APPEND <list><element> [<element> ...]) 添加新element到list中
#CMAKE_MODULE_PATH: 指定要由CMake模块加载的CMake模块的搜索路径include() 要么 find_package()命令,然后检查CMake随附的默认模块。
#{PROJECT_SOURCE_DIR}为包含PROJECT()的最近一个CMakeLists.txt文件所在的文件夹
#cmake_modules:用于找寻不知名库时,事先在CMakeLists.txt相同位置建立一个叫cmake_modules的文件夹,在里面创建寻找库并命名的指令
#设置OpenCV的目录
set(OpenCV_DIR /home/z/cmake-3.4.9/build)
find_package(OpenCV 3.4.9 QUIET)
if(NOT OpenCV_FOUND)
message(FATAL_ERROR "OpenCV > 2.4.3 not found.")
endif()
#找到各种头文件以及源码
find_package(Eigen3 3.1.0 REQUIRED)
find_package(Pangolin REQUIRED)
include_directories(
#提供头文件路径它提供了一个搜索头文件暂时的根目录,即你可以在cmakelists中写上
#include_directories(/usr/local/include)来让库文件搜索以/usr/local/include为基础,即在main函数前写上#include “opencv/cv.h"即可
${PROJECT_SOURCE_DIR}
#{PROJECT_SOURCE_DIR}为包含PROJECT()的最近一个CMakeLists.txt文件所在的文件夹
${PROJECT_SOURCE_DIR}/include
${EIGEN3_INCLUDE_DIR}
${Pangolin_INCLUDE_DIRS}
)
#设置变量到工程lib下 构建时将所有LIBRARY目标放置的位置
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
)
#指定可执行文件的输出位置 CMAKE_RUNTIME_OUTPUT_DIRECTORY: 构建RUNTIME目标文件的输出目录
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})