深入浅出CMake(一):基础篇
深入浅出CMake(二):基础语法及实现九九乘法表
有了前面两篇博文的基础,相信我们已经能够用 CMake 去编译比较简单的工程了,但是我们还差一步就可以应付大多数开发编译场景。
目前我们还不知道怎么样处理在工程中引入第三方库。
以 Unix 环境下的 c 代码工程为例。
加入工程要引入一个 libtest.so 动态库,源码是 hello.cpp。
如果用 g 编译的话,大概是这样
代码语言:javascript复制g hello.cpp -ltest -o hello -I include
引入库文件时,我们必须知道头文件的路径还有库文件的路径
如果我们工程引入的不止一个库文件,而是 20 个的话,用最原始的 g 命令行方式就比较痛苦,也容易出错,所以我们大多会编写 Makefile 来利用 make 编译。
但是 Makefile 的编写有时候也是一件痛苦的事情,用 CMake 可以比较轻松实现同样的目的,所以这篇博文我们讲解如何用 cmake 处理库文件的引入情况。
package
在 cmake 的编译体系中,package 用来指代一个依赖库,包括一些头文件、动态库、静态库等等,在 CMakeFileLists.txt 中通过 find_package()
命令可以很轻易实现。
比如很多同学可能会用 OpenCV 进行图像开发,它是个非常有名的开源库,它有 10 来个库文件、几十个头文件,因此如何正确引用它就是一件值得小心翼翼的事情,但好在 cmake 可以轻松搞定这件事情。
前提条件,你的 PC 上已经完全安装好了 OpenCV
DisplayImage.cpp
代码语言:javascript复制#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>n");
return -1;
}
Mat image;
image = imread( argv[1], 1 );
if ( !image.data )
{
printf("No image data n");
return -1;
}
namedWindow("Display Image", WINDOW_AUTOSIZE );
imshow("Display Image", image);
waitKey(0);
return 0;
}
下面是 CMakeFileLists.txt
代码语言:javascript复制cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( DisplayImage DisplayImage.cpp )
target_link_libraries( DisplayImage ${OpenCV_LIBS} )
然后就可以编译了。
代码语言:javascript复制mkdir build
cd build
cmake ..
make
./DisplayImage ../lena.jpg
程序就可以运行。
这个例子是 OpenCV 的官方示例。
下面需要说明的是,find_package(OpenCV REQUIRED)
执行成功后,它的头文件路径,库文件路径都会被赋值。
OpenCV_INCLUDE_DIRS 是头文件路径
OpenCV_LIBS 是库文件地址
有了头文件和库文件,自然而然可以正常引用 OpenCV 了。
package 的通用套路
实际上,通过 find_package() 可以顺利查找任何符合 cmake package 标准的外部工程。
find_pacage()
方法签名如下:
find_package(<package> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
QUITE 参数指代,当查找出错时,屏蔽 message() 的输出。
REQUIRED 参数指代,当找不到 package 时,终止过程。
现在用 XXX 代表要查找的 package 名字
find_pacakge(XXX REQUIRED)
会设置一系列变量。
XXX_FOUND 代表库是否查找成功
XXX_INCLUDE_DIRS 代表头文件的路径
XXX_LIBRARIES 代表库文件的路径
比如,cmake 可以很轻松引入 bzip2
代码语言:javascript复制find_package (BZip2)
if (BZIP2_FOUND)
include_directories(${BZIP_INCLUDE_DIRS})
target_link_libraries (test ${BZIP2_LIBRARIES})
endif (BZIP2_FOUND)
BZIP2 定义了一系列变量,含义如下
代码语言:javascript复制BZIP2_FOUND - system has BZip2
BZIP2_INCLUDE_DIR - the BZip2 include directory
BZIP2_LIBRARIES - Link these to use BZip2
BZIP2_NEED_PREFIX - this is set if the functions are prefixed with BZ2_
BZIP2_VERSION_STRING
到这里,我们已经掌握了 find_package()
到这里,我们才真正可以应付大多数情况下cmake 编译了。
当然,一个库可能由好多个组件构成,cmake 可以单独引入这些组件
代码语言:javascript复制find_package(Qt5 5.1.0 COMPONENTS Widgets Xml Sql)
引入了 Qt5 中的 Widgets Xml Sql 组件。
可能有同学好奇,cmake 怎么就人工智能一般能够自动找到依赖库呢?
这个,我下一篇详细讲解一下。
参考:
https://cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html