CMake編程實踐(六) 模塊使用和自定義模塊

模塊使用和自定義模塊

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 <iostream>
#include <curl/curl.h>
#include <utilsbox.h>

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工程,這篇就到這裏吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章