深入浅出CMake(三):find_package 添加依赖库

2019-06-16 10:24:51 浏览数 (1)

深入浅出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)执行成功后,它的头文件路径,库文件路径都会被赋值。

代码语言:javascript复制
OpenCV_INCLUDE_DIRS 是头文件路径

OpenCV_LIBS 是库文件地址

有了头文件和库文件,自然而然可以正常引用 OpenCV 了。

package 的通用套路

实际上,通过 find_package() 可以顺利查找任何符合 cmake package 标准的外部工程。

find_pacage()方法签名如下:

代码语言:javascript复制
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)会设置一系列变量。

代码语言:javascript复制
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

0 人点赞