CMake模块的使用和自定义模块

Posted Lion Long

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CMake模块的使用和自定义模块相关的知识,希望对你有一定的参考价值。

CMake模块的使用和自定义模块

一、前言

本文将着重介绍系统预定义的Find 模块的使用以及自己编写Find 模块,系统中提供了其他各种模块,一般情况需要使用INCLUDE 指令显式的调用,FIND_PACKAGE 指令是一个特例,可以直接调用预定义的模块。

其实使用纯粹依靠cmake 本身提供的基本指令来管理工程是一件非常复杂的事情,所以, cmake 设计成了可扩展的架构,可以通过编写一些通用的模块来扩展cmake.

本文首先介绍一下cmake 提供的 FindCURL 模块的使用。然后,基于共享库编写一个FindHello.cmake 模块。

二、使用Find模块

FIND_PACKAGE 指令:

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])

可以使用多种参数,QUIET 参数,REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。

2.1、准备工作

(1)建立 t5 目录,用于存放我们的CURL 的例子。

mkdir t5

(2)建立src 目录,并建立src/main.c

cd t5
mkdir src
cd src
vim main.c

内容如下:

#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE *fp = NULL;
int write_data(void *ptr, size_t size, size_t nmemb, void *stream) 
  int written = fwrite(ptr, size, nmemb, (FILE *)fp);
  return written;

int main(void) 
  const char * path = "/tmp/curl-test";
  const char * mode = "w";
  fp = fopen(path, mode);
  curl_global_init(CURL_GLOBAL_ALL);
  CURL *curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, "http://www.baidu.com");
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  CURLcode res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
  return 0;

这段代码的作用是通过curl 取回http://www.linux-ren.org 的首页并写入/tmp/curl-test文件中。

(3)建立主工程文件CMakeLists.txt。

cd t5
vim CMakeLists.txt

内容如下:

PROJECT(CURLTEST)
ADD_SUBDIRECTORY(src)

(4)建立src/CMakeLists.txt,内容如下:

ADD_EXECUTABLE(curltest main.c)

现在自然是没办法编译的,我们需要添加 curl 的头文件路径和库文件。

2.2、添加头文件路径和库文件

(1)方法1:

直接通过INCLUDE_DIRECTORIES 和TARGET_LINK_LIBRARIES 指令添加:我们可以直接在src/CMakeLists.txt 中添加:

INCLUDE_DIRECTORIES(/usr/include) 
TARGET_LINK_LIBRARIES(curltest curl)

然后建立build 目录进行外部构建即可。

(2)方法2,使用FindCURL 模块。

现在是使用cmake 提供的 FindCURL 模块,向 src/CMakeLists.txt 中添加:

FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES($CURL_INCLUDE_DIR)
TARGET_LINK_LIBRARIES(curltest $CURL_LIBRARY)
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)

对于系统预定义的Find.cmake 模块,使用方法一般如上例所示。每一个模块都会定义以下几个变量

<name>_FOUND
<name>_INCLUDE_DIR or <name>_INCLUDES
<name>_LIBRARY or <name>_LIBRARIES

可以通过_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。

如果< name>_FOUND 为真,则将< name>_INCLUDE_DIR 加入INCLUDE_DIRECTORIES,将< name >_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。

然后建立build 目录进行外部构建:

mkdir build
cd build
cmake ..

如果库不存在,则会报错【CMake Error at src/CMakeLists.txt:7 (MESSAGE): CURL library not found】:

-- The C compiler identification is GNU 8.4.0
-- The CXX compiler identification is GNU 8.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR) 
CMake Error at src/CMakeLists.txt:7 (MESSAGE):
  CURL library not found


CMake Warning (dev) in CMakeLists.txt:
  No cmake_minimum_required command is present.  A line of code such as

    cmake_minimum_required(VERSION 3.21)

  should be added at the top of the file.  The version specified may be lower
  if you wish to support older CMake versions for this project.  For more
  information run "cmake --help-policy CMP0000".
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring incomplete, errors occurred!
See also "/home/fly/workspace/cmakeProj/t5/build/CMakeFiles/CMakeOutput.log".

2.3、< name >_FOUND 来控制工程特性

再来看一个复杂的例子,通过_FOUND 来控制工程特性:

SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
FIND_PACKAGE(JPEG)
 
IF(JPEG_FOUND)
SET(optionalSources $optionalSources jpegview.c)
INCLUDE_DIRECTORIES( $JPEG_INCLUDE_DIR )
SET(optionalLibs $optionalLibs $JPEG_LIBRARIES )
ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)
 
IF(PNG_FOUND)
SET(optionalSources $optionalSources pngview.c)
INCLUDE_DIRECTORIES( $PNG_INCLUDE_DIR )
SET(optionalLibs $optionalLibs $PNG_LIBRARIES )
ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT)
ENDIF(PNG_FOUND)
 
ADD_EXECUTABLE(viewer $mySources $optionalSources ) TARGET_LINK_LIBRARIES(viewer $optionalLibs

通过判断系统是否提供了JPEG 库来决定程序是否支持JPEG 功能。

三、编写自定义的Find模块

3.1、 准备工作

编写属于自己的FindHello 模块。

(1)建立cmake/中建立t6 目录,并在其中建立cmake 目录用于存放我们自己定义的FindHELLO.cmake 模块。同时建立src 目录,用于存放我们的源文件。

mkdir t6
cd t6
mkdir cmake
mkdir src

(2)定义 cmake/FindHELLO.cmake 模块。

FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib)

IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
    SET(HELLO_FOUND TRUE)
ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)

IF (HELLO_FOUND) 
    IF (NOT HELLO_FIND_QUIETLY)
        MESSAGE(STATUS "Found Hello: $HELLO_LIBRARY")
    ENDIF (NOT HELLO_FIND_QUIETLY)
ELSE (HELLO_FOUND)
    IF (HELLO_FIND_REQUIRED)
        MESSAGE(FATAL_ERROR "Could not find hello library")
    ENDIF (HELLO_FIND_REQUIRED)
ENDIF (HELLO_FOUND)

可以使用多种参数,QUIET 参数,对应与我们编写的 FindHELLO 中的 HELLO_FIND_QUIETLY,如果不指定这个参数,就会执行:

MESSAGE(STATUS "Found Hello: $HELLO_LIBRARY")

REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于 FindHELLO.cmake 模块中的HELLO_FIND_REQUIRED 变量。

在上面的模块中定义了HELLO_FOUND, HELLO_INCLUDE_DIR,HELLO_LIBRARY 变量供开发者在FIND_PACKAGE 指令中使用。

3.2、cmake 模块

hello的库使用之前文章介绍的示例。

下面建立src/main.c,内容为:

#include <hello.h>
int main()

    HelloFunc();
    return 0;
 

建立src/CMakeLists.txt 文件,内容如下:

FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
ADD_EXECUTABLE(hello main.c)
INCLUDE_DIRECTORIES($HELLO_INCLUDE_DIR)
TARGET_LINK_LIBRARIES(hello $HELLO_LIBRARY)
ENDIF(HELLO_FOUND)

为了能够让工程找到FindHELLO.cmake 模块(存放在工程中的 cmake 目录),我们在主工程文件CMakeLists.txt 中加入:

SET(CMAKE_MODULE_PATH $PROJECT_SOURCE_DIR/cmake)

3.3、使用自定义的FindHELLO 模块构建工程

仍然采用外部编译的方式,建立build 目录,进入目录运行:

mkdir build
cd build
cmake ..

我们可以从输出中看到:

Found Hello: /usr/lib/libhello.so

如果把上面的FIND_PACKAGE(HELLO)修改为FIND_PACKAGE(HELLO QUIET),则不会看到上面的输出。

接下来就可以使用make 命令构建工程,运行:

./src/hello 

可以得到输出:

Hello World

说明工程成功构建。

3.4、如果没有找到hello library

我们可以尝试将/usr/lib/libhello.x 移动到/tmp 目录,这样,按照 FindHELLO 模块的定义,就找不到hello library 了,我们再来看一下构建结果:

cmake ..

仍然可以成功进行构建,但是这时候是没有办法编译的。

修改FIND_PACKAGE(HELLO)为FIND_PACKAGE(HELLO REQUIRED),将hello library 定义为工程必须的共享库。

这时候再次运行cmake …

我们得到如下输出:

CMake Error: Could not find hello library.

因为找不到libhello.x,所以,整个Makefile 生成过程被出错中止。

四、总结

使用系统提供的 Find< NAME>模块并学习编写Find< NAME>模块以及如何在工程中使用这些模块。

以上是关于CMake模块的使用和自定义模块的主要内容,如果未能解决你的问题,请参考以下文章

调试不适用于 Android Studio 的 C++/本机库模块(使用 Cmake)

cmake 文件结构

CMAKE将动态库链接到模块,但不显示为链接依赖

CMake:覆盖查找模块

在qbs项目中添加子模块(使用cmake构建)

cmake一个模块包括另一个模块