详见 https://cmake.org
Cross platform Make
CMakeLists.txt
文件来制定整个编译流程CMake
专用定义文件,文件名严格区分大小写。在工程方面,可以每个目录都存放一个CMakeLists.txt
文件,也可以只存放一个CMakeLists.txt
文件,管理所有目录,也可以都用。设定完CMakeLists.txt
,CMake 在终端涉及到的语法都是针对 CMakeLists
所在的位置,如cmake ..
其中的..
就表示所要执行的CMakeLists
与当前目录的位置关系。
通常来说,${}
是用来引用变量的,但是对于IF
控制语句来说,变量的引用直接通过变量名来引用,而不需要用${}
,如果使用了${}
,则IF
会判断名为${}
所代表的值的变量(该变量当然不存在)。
指令是大小写无关的,但参数和变量是大小写相关的。
当所引用的文件为多个时,可以通过“ ”
或者;
进行引用。
cmake
是构建定义文件CMakeLists.txt
的,make
是编译cmake
生成的MakeFile
文件,并完成链接(相当于IDE中的build
,实际上build只是链接的意思,但在IDE中一般是编译+链接。修改头文件,需要rebuild
)。其中,make -j
表示默认与CPU相匹配的线程进行同步编译,也可以指定线程编译cmake -j8
。
cmake后的-D
相当于定义,定义-D
后参数给系统。
文件夹的命名和作用:
library
是库文件,include
是头文件,src
是源文件(该源文件不一定是可执行程序的所需的源文件,也可以是生成所需的内部库的源文件)。
CMakeLists.txt:
library
是库的意思, include
是包含,directory
是路径。
CMAKE_MINIMUM_REQUIRED
cmake_minimum_required (VERSION 2.8)
指定最小的 cmake 版本,若小于 2.8 版本要求,则程序终止运行。
PROJECT
project (project_name [CXX] [C] [Java])
指定工程名称和所用语言,该指令隐式的定义了两个cmake
变量:
和
,同时也预定义了PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
变量。上述对应变量为同一值。方便起见,就用后者代替前者。SOURCE
是指源文件,BINARY
是指二进制可执行文件(编译)。
还预定义了其他指令:
PROJECT_NAME
:项目名称,即为project(xxxx)
中的xxxx
PROJECT_SOURCE_DIR
:表示含project()
指令的CMakeLists.txt
所在的文件夹EXECUABLE_OUTPUT_PATH
:可执行文件的输出路径LIBRARY_OUTPUT_PATH
:库文件的输出路径CMAKE_BINARY_DIR
:默认 build 文件夹所在的绝对路径CMAKE_SOURCE_DIR
:源文件所在的绝对路径SET
set(variable value)
变量替代值 ,如set(SRC_LST main.cpp other.cpp)
表示定义SRC_LST
替代后两个 cpp 文件。
重新定义EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
来指定最终二进制文件的位置(最终生成的二进制文件或者共享库文件,无中间文件)。
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
CMAKE_CXX_FLAGS
编译C++的设置标志,后接-std=c++11
用c++11 的标准,或接-Wall
(Warning all)就不会输出warning
信息。因为CMAKE_CXX_FLAGS
变量-Wall
被-std=c++11
替换掉了。
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-std=c++11") #后者会将前者顶掉,只留下后者
改为
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")#第一个CMAKE_CXX_FLAGS表示-Wall
set(CMAKE_CXX_STANDARD 11)
是高版本,
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
是低版本。
-march=native
选项的作用是告诉编译器使用当前系统的本地处理器架构,以生成针对该架构优化的代码,从而提高程序的性能。
set(CMAKE_CXX_FLAGS "-O3")
,其中"-O3"
或者改成"-O0"
,"-O3"
表示优化程度最高,-O0
表示不需要优化。注意O字母要大写
最新版本。set(CMAKE_CXX_STANDARD 20)
的位置在哪都可以
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
CMAKE_BUILD_TYPE
有多种模式,Debug
调试模式,输出调试信息,不做优化;Release
发布模式,没有调试信息,全优化;RelWithDebInfo
类似 Release模式,但包含了调试信息;MinSizeRel
一种特殊的 Release 模式,会特别优化库的大小。
MESSAGE
message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ... )
指令用于像终端传输用户信息。SEND_ERROR
产生错误,生成过程被跳过;STATUS
输出前缀为--
的信息;FATAL_ERROR
立即终止所有cmake过程,也可以直接输出信息message("message to display ...")
,就没有STATUS
的前缀 --
符号。
ADD_SUBDIRECTORY
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
向当前工程添加存放源文件的子目录,并指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL
是将此目录从编译过程中排除。
用于多个CMakeLists
文件使用。
add_subdirectory(src bin)
指定将src
子目录加入工程,并指定编译输出(包含编译中间结果)路径为bin
目录(在bulid
文件下,属于build
文件的子目录);若add_subdirectory(src)
没有给出bin
目录,则指定的目录名称为src
。
ADD_LIBRARY
定义了库文件,add_library( libname [SHARED|STATIC|MODULE] [EXECLUDE_FROM_ALL] source1 source2 ...)
对于libname
来说,只需写出name
,无需写出libname.so
即可。
库分别对应为动态库,静态库和dyld系统
静态库
原理:编译时将源代码复制到程序中,运行时不用库文件仍可运行,参与链接。
优点:运行已有代码,运行时不用库,速度更快
缺点:占用更多的空间和磁盘,静态库升级时需要重新编译程序
动态库/共享库(常用)
原理:编译时仅记录使用的库和使用的二进制符号,不复制相关代码,参与运行
优点:不复制代码,占用空间小;多个程序可以调用一个库;升级方便,无需重新编译
缺点:程序运行需要加载库,耗费时间
系统 | 静态库 | 共享库 |
---|---|---|
Windows | lib | .dll |
Linux | .a | .so |
Mac OS | .a | dylib |
若生成的库本身依赖其他库,需要add_library(libname *.cpp)
之后,再target_link_libraries(libname 依赖库)
ADD_EXECUTABLE
add_executable(name ${SRC_LIST})
定义了可执行文件的名称和可执行文件的相关源文件
add_executable(main main.cc)
add_executable(source source.cc)
add_executable(main main.cc source.cc)
前两行代码是分别构造两个可执行程序。每个可执行程序对应一个主函数int main()
最后一行的代码是将两个编译单元整合到成一个可执行程序,其中 main.cc
和 source.cc
只能有一个主函数int main()
函数。如果超过一个翻译单元调用了头文件,该头文件中函数设置为inline
。
ADD_DEFINITIONS
add_definitions(-DFOO -DBAR ...)
His command can be used to add any flags, but it is intended to add preprocessor definitions
add_definitions(-w)
编译时不显示warning信息
SET_TARGET_PROPERTIES
set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ... )
可以用来指定输出名称,set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
则输出libhello.a
库,也可以控制动态库的版本。
LINK_DIRECTORIES
link_directories(directory1 directory2 ...)
添加非标准的共享库搜索路径
INCLUDE_DIRECTORIES
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
向工程添加多个特定的头文件搜索路径
link和include区分,前者为链接就是库所用的,后者是包含就是头文件所用#include <**.hpp>
。
如文件main.cpp
用到路径/usr/local/include/opencv/cv.h
这个文件时,CMakeLists.txt
中添加include_directories(/usr/local/include)
,并在使用main.cpp
写上#include "opencv/cv.h"
即可
LIST
list(APPEND
添加 element 到 list 中。如 [
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
find_package(**** REQUIRED)
find_package(**** REQUIRED)
其中cmake_modules/
中有可供搜索的*.cmake
FIND_PACKAGE
采用两种模式(FindXXX.cmake
和XXXConfig.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
模式,若没找到,会改为Config
模式;如果XXX_DIR
路径下找不到XXXConfig.cmake
文件,则会找/usr/local/lib/cmake/XXX/
中的XXXConfig.cmake
文件。总之,Config
模式是一个备选策略,通常库安装时会拷贝一份XXXConfig.cmake
到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
若XXX安装时没有安装到系统目录中,则无法自动找到XXXConfig.cmake
,需要在CMakeLists
最前面添加XXX的搜索路径,set(XXX_DIR XXXbuild的路径)
,也可以使用指定版本库。
find_package(package version EXACT/QUIET/REQUIRED)
寻找cmake
提供的某个库的头文件与库文件的指令,version
指定查找库的版本号;EXACT
要求该版本号必须精确匹配;QUIET
禁掉没有找到时的警告信息;REQUIRED
版本号以上,没有找到则停止CMake,并输出警告信息。
一般find_package()
紧挨着include_directories()
find_package(OpenCV 3 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
注意:寻找多个路径时,需要添加对应寻找路径个数的find_package
,不能用一个find_package
包含所有路径。因为是package
而不是packages
。
OpenCV
大小写和${NAME_INCLUDE_DIRS}
在target_link_libraries( ... ${NAME_LIBS}) DIRS
和LIBS
。
当find_package(
找到库时,会初始化以下变量
_FOUND
:是否找到库的标记_INCLUDE_DIRS
或者_INCLUDES
:头文件路径_LIBRARIES
或_LIBS
:库文件_DEFINITIONS
:定义TARGET_LINK_LIBRARIES
target_link_libraries( target library1
为target
添加需要链接的共享库
示例1(orb-slam2的部分代码)
一个CMakeLists.txt
文件
..
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}
)
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
)
..
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})
示例2
一个CMakeLists.txt
文件
# CMake 最低版本号要求
cmake_minimum_required( VERSION 2.8 )
# 项目名称
project( image_undistort )
# 设置编译方式
set( CMAKE_BUILD_TYPE Release )
#支持C++11
set( CMAKE_CXX_FLAGS "-std=c++11" )
# 查找系统里的OpenCV
find_package( OpenCV 3 REQUIRED )
# 添加项目包含路径
include_directories(
${PROJECT_SOURCE_DIR}/include/
${OpenCV_INCLUDE_DIRS}
)
message("PROJECT_SOURCE_DIR = " ${PROJECT_SOURCE_DIR})
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
message("CMAKE_BINARY_DIR = " ${CMAKE_BINARY_DIR})
message("CMAKE_SOURCE_DIR = " ${CMAKE_SOURCE_DIR})
# 将指定的源文件生成链接库文件。STATIC 为静态链接库,SHARED 为共享链接库
add_library(lib_undistort SHARED ${PROJECT_SOURCE_DIR}/source/undistort.cpp)
# 为库或可执行文件添加库链接
target_link_libraries( lib_undistort
${OpenCV_LIBS}
)
# 用指定的源文件为工程添加可执行文件,名字为undistort_image
add_executable( ${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/testBed/main.cpp )
# 为库或可执行文件添加库链接
target_link_libraries( ${PROJECT_NAME}
${OpenCV_LIBS}
lib_undistort
)
示例三
多个CMakeLists.txt
文件
https://github.com/gaoxiang12/slambook2/blob/master/ch13
FOREACH
循环指令
格式1:
foreach(<Loop_var> <items>)
endforeach()
如
foreach(i 0 1 2 3)
message(STATUS "current is ${i}")
endforeach(i)
格式2:
foreach(<Loop_var> RANGE start stop [step])
endforeach()
如
foreach(i RANGE 0 3 1)
message(STATUS "current is ${i}")
endforeach(i)
一种是在编译后直接make install
,另一种是打包时,指定的目录安装。通过make install
直接安装,或者可以通过输入make install DESTDIR=/../..
指定安装路径。
可以在cmake
之后生成的CMakeCache.txt
中查找。make install
默认路径为/usr/bin
。make clean
对之前的缓存进行清理,不需要重新建立build等指令。
CMAKE_INSTALL_PREFIX
cmake指令安装的路径:cmake -DCMAKE_INSTALL_PREFIX=/... ..
,,可以安装到自定义路径中,对于CMAKE_INSTALL_PREFIX
默认路径为/usr/local
INSTALL
定义打包安装规则,安装的内容可以有目标二进制、动/静态库、以及文件、目录、脚本等。
目标文件(通过ADD_EXECUTABLE
或者ADD_LIBRARY
定义的可执行二进制、动/静态库)的安装:
install(TARGETS file_names ... [ARCHIVE|LIBRARY|RUNTIME][DESTINATION
目标文件分别为通过ADD_EXECUTABLE
或者ADD_LIBRARY
生成的对应类型为静态库、动态库和可执行的目标二进制;DESTINATION
定义了安装的路径,如果使用CMAKE_INSTALL_PREFIX
来安装,则注意将路径写为相对路径,对应安装的路径为${CMAKE_INSTALL_PREFIX}/
。
普通文件(*.txt文件等)的安装:
install(FILES files ... DESTIANTION
非目标文件的可执行程序的安装(脚本类):
install(PROGRAMS files ...DESTINATION
目录的安装:
install(DIRECTORY dirs ... DESTINATION
dirs
是相对于
的目录,但对于dirs
来说,dirs
和dirs/
是不同的,前者是将整个目录都作为子目录置于
中包含目录本身文件夹,后者是将整个目录中的内容作为文件置于
中,不包含目录本身文件夹。
1
make
时出现*****.h : No such file or directory .
该头文件没有包含到project中,使用include_directories()
将该头文件所在的目录加入工程中。
2
若找不到*.cpp
中的头文件,寻找多个路径时,需要添加对应寻找路径个数的find_package
,不能用一个find_package
包含所有路径。
3
如果已经编译完成的库,find_package
等找不到,可能是对应的库没有安装到系统中,只是安装到相应的文件夹中。需要通过sudo make install
来安装到系统中。
4
输入sudo make install
报错:make: *** No rule to make target 'install'. Stop.
表示对应的CMakeLists
文件内没有install
指令,无法执行安装。