CMake编程实践(六) 模块使用和自定义模块

文章目录

    • 模块使用和自定义模块
      • 使用系统预定义的FindCURL模块
      • 编写自定义的UtilsBox模块
      • 编译执行
      • 小结

模块使用和自定义模块

cmake开发者在开发过程中认为纯粹依靠cmake本身提供的基本指令来管理工程是也一件非常复杂的事情,所以cmake设计成了可扩展的架构,可以通过编写一些通用的模块来扩展cmake, 系统中提供了其他各种模块,一般情况需要使用INCLUDE指令显式的调用,FIND_PACKAGE指令是一个特例,可以直接调用预定义的模块。

在本篇,将先介绍cmake提供的FindCURL模块的使用。然后,基于我们上节的libutilsbox共享库,编写一个FindUTILSBOX.cmake 模块。

注意:FIND_PACKAGE调用预定义在 CMAKE_MODULE_PATH 下的 Find.cmake 模块,如果我们要调用UtilsBox模块,则要写成FindUTILSBOX.cmake,文件名是大小写敏感的,注意UtilsBox模块名称要全部大写。否则会提示找不到,你可以尝试把FindUTILSBOX.cmake中的UTILSBOX改为小写,然后重新cmake … 看看会提示什么。

本篇源码在cmake-curl-utilsbox项目中,通过目录树可以明显看出多了cmake目录以及FindUTILSBOX.cmake
cmake-curl-utilsbox项目中同时使用系统提供的系统预定义的FindCURL模块以及自定义的FindUTILSBOX模块,
需要依赖上节安装的utilsbox库。

目录树:

.
├── cmake
│ └── FindUTILSBOX.cmake
├── CMakeLists.txt
├── doc
│ └── Introduction.txt
├── README.md
└── src
    ├── CMakeLists.txt
    └── main.cpp

CMakeLists.txt

# 声明编译要求cmake最低版本
cmake_minimum_required(VERSION 3.0)

# 声明一个cmake工程
project(CURLTEST)

# 添加子目录, 并指定指定中间二进制和目标二进制存放的位置
ADD_SUBDIRECTORY(src bin)

src/main.cpp

#include 
#include 
#include 

using namespace std;

FILE *fp;

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(int argc, char** argv)
{
 const char * path = "/tmp/curl-test";
 const char * mode = "w";
 fp=fopen(path, mode);
 curl_global_init(CURL_GLOBAL_ALL);
 CURLcode res;

 CURL *curl = curl_easy_init();
 if(!curl) {
  cout << "curl init error."<< argc<< endl;
  return -1;
 }

 curl_easy_setopt(curl, CURLOPT_URL, "https://cmake.org/");
 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
 res = curl_easy_perform(curl);

 cout << "curl res:" << res << endl;
 curl_easy_cleanup(curl);

 cout << UtilsBox::format("UtilsBox version:%s",UtilsBox::GetVersion().c_str())<< endl;

 return 0;
}

src/CMakeLists.txt

# 声明编译要求cmake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)


# 添加源文件
SET(SOURCE 
    main.cpp)

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# 添加一个可执行程序,名称和工程名称保持一致
ADD_EXECUTABLE(curltest ${SOURCE})

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

MESSAGE(STATUS "This is BINARY dir" ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir" ${PROJECT_SOURCE_DIR})

# 方案1:
# INCLUDE_DIRECTORIES(/usr/include)
# TARGET_LINK_LIBRARIES(curltest curl)

# 方案2:
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
    INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
    TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE()
    MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF()


FIND_PACKAGE(UTILSBOX)
IF(UTILSBOX_FOUND)
    MESSAGE(STATUS "UTILSBOX_FOUND")
    INCLUDE_DIRECTORIES(${UTILSBOX_INCLUDE_DIR})
    TARGET_LINK_LIBRARIES(curltest ${UTILSBOX_LIBRARY})
ENDIF()

添加一个库可以用多种方案,比如上面的方案1:我们已经知道库的位置已经头文件定义的位置,那么直接可以使用方案1来引用这个库,简单省事,但多情况下我们不知道库具体安装在哪个目录,尤其是有些库是自己手动编译安装的,那对于用这个库的人来说就是灾难,所以就有了方案2:通过加载一个给定的cmake模块以及库名就可以快速加载到指定的库,恩 真香~

使用系统预定义的FindCURL模块

对于系统预定义的 Find.cmake 模块,使用方法一般如下所示:
每一个模块都会定义以下几个变量
• _FOUND
• _INCLUDE_DIR or _INCLUDES
• _LIBRARY or _LIBRARIES
你可以通过_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭
某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。
如果_FOUND 为真,则将_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,
将_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。

例如上述代码中使用的FindCURL模块

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

我们再来看一个复杂的例子,通过_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 功能。

编写自定义的UtilsBox模块

我们在上节构建了动态库、静态库并进行了安装。接下来,我们将演示如何自定义UtilsBox模块并使用这个模块构建工程:

在cmake-curl-utilsbox中可以看到
cmake目录中有一个FindUTILSBOX.cmake

FIND_PATH(UTILSBOX_INCLUDE_DIR utilsbox.h /usr/include /usr/local/include)
FIND_LIBRARY(UTILSBOX_LIBRARY NAMES utilsbox PATH /usr/lib /usr/local/lib)

IF(UTILSBOX_INCLUDE_DIR AND UTILSBOX_LIBRARY)
    SET(UTILSBOX_FOUND TRUE)
ENDIF()

IF(UTILSBOX_FOUND)
    IF (NOT UTILSBOX_FIND_QUIETLY)
        MESSAGE(STATUS "Found UtilsBox: ${UTILSBOX_LIBRARY}")
    ENDIF()
ELSE()
    IF (UTILSBOX_FIND_REQUIRED)
        MESSAGE(FATAL_ERROR "Could not find utilsbox library")
    ENDIF()
ENDIF()

cmake-curl-utilsbox/src/CMakeLists.txt
中的如下代码,用来搜索和添加utilsbox库

FIND_PACKAGE(UTILSBOX)
IF(UTILSBOX_FOUND)
    INCLUDE_DIRECTORIES(${UTILSBOX_INCLUDE_DIR})
    TARGET_LINK_LIBRARIES(curltest ${UTILSBOX_LIBRARY})
ENDIF()

编译执行

cd cmake-curl-utilsbox
mkdir build && cd build
cmake ..
make
./bin/curltest
➜ build ./bin/curltest 
* Trying 66.194.253.19...
* Connected to cmake.org (66.194.253.19) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=cmake.org
* start date: Mar 3 14:37:12 2020 GMT
* expire date: Jun 1 14:37:12 2020 GMT
* subjectAltName: cmake.org matched
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
> GET / HTTP/1.1
Host: cmake.org
Accept: */*

< HTTP/1.1 200 OK
< Date: Sat, 25 Apr 2020 06:16:20 GMT
< Server: Apache
< X-Frame-Options: SAMEORIGIN
< Last-Modified: Fri, 24 Apr 2020 22:32:51 GMT
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
< 
* Connection #0 to host cmake.org left intact
curl res:0
UtilsBox version:v0.1.1

cmake-demo源码已上传到github,欢迎Star

小结

本篇主要讲述了如何使用系统预定义的cmake模块以及使用自定义的cmake模块,相信对cmake的认知又深了一些。后面我会分析一些热门开源项目的CMake工程,这篇就到这里吧。

你可能感兴趣的:(C/C++,linux,CMake)