写在前面
近来进行G2O优化库的学习,第一个demo程序就被cmake坑的很惨,之前对于cmake总感觉就是用着学着,但是这次踩坑之后发现这样的方式确实会浪费一些时间,特别是没有人带的情况下,但是现阶段自己也没能拿出大块的时间去学习cmake,所以特别的写这个博客,希望在以后的时间里能对cmake的知识进行进一步的完整,相信慢慢的积累总能达到一些效果。
关于find_package
这个命令是cmake里面应该算是最熟悉的命令了,最开始写opencv程序的时候就会照着别人的cmakelists写find_package(OpenCV REQUIRED),当时也试过,写成OPENCV或者opencv都会出问题,但是也没有深究,现在仔细研究了一下,算是补上当时的偷懒吧。
find_package的查找顺序
在cmake里面,find_package是有相应的查找顺序的,首先cmake会在模块路径下进行查找Find<Module>.cmake文件(这个文件名都是这样的,这里“<Module>”的内容就是find_package里面包的名字),具体的查找路径为:
1. 变量CMAKE_MODULE_PATH中的所有路径;
2. 查找cmake自己的模块库,位置在/usr/share/cmake-x.x/Modules里面;
3. ~/.cmake/packages/或者/usr/local/share/中的各个包目录下查找<Module>Config.cmake(OpenCV就在这个目录下);
那么在上述的过程中,首先我们必须知道说自己要找的模块名字,也就是上述<Module>的名字,因为这个决定了find_package里面模块的名称;其次就是手动加入查找的路径,比如G2O的程序,我们需要手动的将cmake_module文件夹(这个文件夹g2o提供了,里面有G2O,CSparse,Cholmod模块的.cmake文件)加入到查找路径中,一般使用如下语句把路径加入到查找链表中
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
find_package之后的变量
我们知道,find_package过后,我们可以通过<Module>_FOUND进行是否查找到模块的判断,同样的,如果该标志位为真,则在整个cmakelists中会产生一些变量,多数博客讲一般产生如下变量:
<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
<NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
<NAME>_DEFINITIONS
但是今天发现其实并不是这样的,甚至有时候NAME的大小写都会变化了,那么具体的变量是什么呢?还是要从.cmake里面进行查找,这里以opencv和cholmod为例:
OpenCV的情况
我们先进入到/urs/local/share/OpenCV/文件夹下,打开OpenCVConfig.cmake文件,我们可以从注释中看到
我们可以看到,找到opencv模块之后会产生如下变量(这里列出最常用的):OpenCV_LIBS和OpenCV_INCLUDE_DIRS
所以在cmakelists中我们就要用这两个变量把opencv的库文件和头文件加入进来。
Cholmod的情况
同样的,我们打开Cholmod的.cmake文件,可以看到如下程序
可以看到我们在cmakelists需要使用的变量为CHOLMOD_INCLUDE_DIR和CHOLMOD_LIBRARIES,跟我的经历一样,一旦这个CHOLMOD_INCLUDE_DIR写成CHOLMOD_INCLUDE_DIRS或者Cholmod_INCLUDE_DIR,整个程序就找不到声明和定义。
所以可以看到,cmake里面的变量也不是一成不变的,而是根据.cmake文件的信息来的。
add_custom_command
最近在做项目的时候希望cmake在构建完项目之后可以帮助自己做一些拷贝的事情,自然就看到了这个操作,仔细看了下其实该命令还挺厉害的,基本上构建完成之后或者之前的命令都可以满足一些基本的需求。
官方的文档如下:
https://cmake.org/cmake/help/v3.1/command/add_custom_command.html
可以看到这个命令有两种形式,取决于第一个参数是OUTPUT还是TARGET
OUTPUT情况:这种情况还没有用到,从描述上来看是要输出一个文件,分为几个不同的情况,感觉比较有用的情况是指定一个项目生成文件,可以保证在所有的依赖之前构建。
TARGET情况:这种情况就是当指定的目标(特别是你生成的app或者库时)生成之前、之后或者链接之前,该命令将会执行。
这次主要总结一下该命令都有哪些候选:
可以看到命令还是很多的,基本满足正常的复制,删除,打包和创建等等,其中不乏有对比等的命令,这个地方说实话有点儿蒙,因为这不是shell命令,对比之后是有返回值的还是跟shell一样使用$?,或者是用生成器中的逻辑比较?以后用到了在进行补充。
总结
1. find_package(NAME REQUIRED)中的NAME不是随便写的,而是要根据.cmake前面的模块名字写的;
2. 模块找到后,起产生的变量也不是一成不变或者有规律的,而是.cmake文件里面的变量名;