CMake

CMake指南

        • CMake
        • CMakeLists.txt
        • 需知
        • 编译
        • 安装
        • 注意

详见 https://cmake.org


CMake

Cross platform Make

  • 开源的跨平台自动化建构系统,用来管理程序构建,不相依于特定编译器
  • 编写CMakeLists.txt 文件来制定整个编译流程

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变量:_BINARY_DIR_SOURCE_DIR,同时也预定义了PROJECT_BINARY_DIRPROJECT_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_PATHLIBRARY_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.ccsource.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.cmakeXXXConfig.cmake)搜索外部库。
搜索时有两种模式

  • Module模式:搜索CMAKE_MODULE_PATH指定路径下的FindXXX.cmake 文件,执行该文件从而找到XXX库。其中,具体查找库并给XXX_INCLUDE_DIRSXXX_LIBRARIES两个变量赋值的操作由FindXXX.cmake模块完成
  • Config模式:搜索XXX_DIR指定路径下的XXXConfig.cmake文件从而找XXX库。其中,具体查找库并给XXX_INCLUDE_DIRSXXX_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}) DIRSLIBS

find_package()找到库时,会初始化以下变量

  • _FOUND:是否找到库的标记
  • _INCLUDE_DIRS或者_INCLUDES:头文件路径
  • _LIBRARIES_LIBS:库文件
  • _DEFINITIONS:定义

TARGET_LINK_LIBRARIES
target_link_libraries( target library1 library2 ... )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/binmake 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来说,dirsdirs/是不同的,前者是将整个目录都作为子目录置于中包含目录本身文件夹,后者是将整个目录中的内容作为文件置于中,不包含目录本身文件夹。


注意

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指令,无法执行安装。


你可能感兴趣的:(工具,工具库,c++,CMake,CMakeLists.txt)