(本文借鉴了chaofn的博客,https://www.cnblogs.com/chaofn/p/10160555.html)
一、为什么要使用cmake
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
在做一个比较大型的C/C++多人项目或者是开源项目的时候,我们会共同维护源码,但是大量的.h/.c文件之间也存在着复杂的关系,你可以把源码放到github上共享给他人,而如何使用这些源码来构建一个项目就可以由cmake来说明。它是跨平台的,在windows下写好的代码可以通过cmake,放到linux环境下生成项目,很方便。
在一个cmake工程中,我们会用cmake命令生成一个makefile文件,然后用make命令根据这个makefile文件的内容编译整个工程。这些cmake命令需要写在CMakeLists.txt这个文件中。
二、如何写CMakeLists.txt
- 声明要求的cmake最低版本:
cmake_minimum_required(VERSION 2.6)
声明此cmake要求的最低版本是2.6,对于像我这样的初学者来说,因为只使用最初级的命令,所以哪个版本其实无所谓吧。。
- 声明一个工程,参数是工程的名字:
project(test_project)
- 声明项目的输出目录:
# set the directory of executable files
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
这里将项目的输出目录参数CMAKE_RUNTIME_OUTPUT_DIRECTORY(注意大小写)设置为bin这个文件。(注释行用'#'开头哦)
- 设置编译器编译模式:
set( CMAKE_BUILD_TYPE "Debug" )
将编译模式设为"Debug"模式,当然,如果程序已经没什么问题,或者是想要更快的速度,可以设置为"Release"。不过这项我在使用的时候并没有写。本人使用cmake生成项目文件后,用VS2017编译,生成哪种模式的都可以。
- 添加第三方包,如OpenCV等:
find_package(OpenCV 4 REQUIRED)
find_package(OpenGL)
find_package(GLUT)
find_package
这个指令被用来在系统中自动查找配置构建工程所需的程序库。在linux和unix类系统下这个命令尤其有用。CMake自带的模块文件里有大半是对各种常见开源库的find_package支持,支持库的种类非常多。这个函数第一个参数是库的名称,第二个参数是最低要求的版本,第三个参数声明为REQUIRED说明没有这个库就无法完成编译。
这里尤其要说一下OpenCV,一个是版本问题,如果安装的OpenCV是3.0以上的版本,则一定要加上版本参数,如果还是有误的话,那么需要额外配置一下OpenCV。在 环境变量 中加入新的 系统变量名称为“OpenCV_DIR”,值是你要使用的OpenCV安装目录中的库文件夹所在,比如D:\opencv\build\x64\vc15\lib。
- 添加引用的第三方头文件,例如添加Eigen头文件等:
include_directories("external/eigen")
include_directories("external/MeshLib/core")
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${GLUT_INCLUDE_DIR})
include_directories("include")
这样就能使用eigen或者是MeshLib的库函数啦。要注意的是像"${OpenCV_INCLUDE_DIRS}"这样的头文件,必须要经过上一步的find_package之后才能使用。("${ }"这组符号里面放的是一个变量名称)
- 设置头文件和源文件:
set(headers
include/arcball.h
include/gl_util.h
include/point_util.h
include/voronoi_mesh.h
include/voronoi.h
)
set(sources
src/voronoi.cpp
src/point_util.cpp
src/main.cpp
)
这里又用到了SET这个函数,它可以显式地定义变量。用法如下:
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
这里把"headers"这个变量设置为"include/arcball.h ......",之后可以使用headers来指代这些头文件。
- 编译生成库文件:
add_library(image_util src/image_util.cpp)
这条命令告诉cmake,我们想把这些源文件编译成一个叫作“image_util”的库。库文件分为静态库和动态库两种,静态库以.a作为后缀名,共享库以.so结尾。所有库都是一些函数打包后的集合,差别在于静态库每次被调用都会生成一个副本,而共享库则只有一个副本,更省空间。如果想生成共享库而不是静态库,只需要使用以下语句即可
add_library(image_util_shared SHARED src/image_util.cpp)
像这样声明的库,最后会独立生成一个项目,所以上一步中并没有把“image_util.h”放入headers中。
- 可执行程序的生成命令:
add_executable(reflector ${headers} ${sources})
这个语句说明我要用"headers"和"sources"来生成一个可执行文件。
- 把其他的库链接到可执行文件上:
target_link_libraries(reflector image_util)
target_link_libraries(reflector ${OpenCV_LIBS})
target_link_libraries(reflector ${OPENGL_gl_LIBRARY} ${GLUT_LIBRARIES})
如果不用这个语句链接的话,可执行文件就找不到这些库在哪了。
三、使用CMakeLists.txt来构建工程
在windows中直接使用cmake-gui十分方便。在"source code"这一项中加上CMakeLists.txt所在的路径,下一栏写上要在哪生成工程。之后分别点击"Configure"和"Generate"就能生成一个工程了。