CMake教程(4):添加庫(kù)

在之前的基礎(chǔ)上本文中將講解如何把代碼以庫(kù)的形式引到工程中。在子目錄中寫(xiě)一個(gè)簡(jiǎn)單的數(shù)學(xué)庫(kù),里面實(shí)現(xiàn)一個(gè)平方運(yùn)算方法。本文中用到的CMake函數(shù)盡量只講解本文中涉及到的部分。后面會(huì)專(zhuān)門(mén)會(huì)詳細(xì)介紹平時(shí)常用的函數(shù)。

工程結(jié)構(gòu)

先貼出組織的工程結(jié)構(gòu):

.
├── CMakeLists.txt          ->根目錄CMake配置
├── MathFunctions           ->數(shù)學(xué)庫(kù)目錄
│   ├── CMakeLists.txt      ->數(shù)學(xué)庫(kù)CMake配置
│   ├── MathFunctions.h     ->數(shù)學(xué)庫(kù)頭文件
│   └── MySquare.cpp        ->數(shù)學(xué)庫(kù)實(shí)現(xiàn)文件
├── Tutorial.cpp            ->可執(zhí)行文件源碼
├── TutorialConfig.h        ->工程配置頭文件
└── build                   ->構(gòu)建編譯目錄

實(shí)現(xiàn)數(shù)學(xué)庫(kù)

首先,實(shí)現(xiàn)咱們簡(jiǎn)單的數(shù)學(xué)庫(kù)MathFunctions。數(shù)學(xué)庫(kù)的頭文件 MathFunctions.h目前只聲明一個(gè)函數(shù)。內(nèi)容為:

double mySquare(double inputValue);

數(shù)學(xué)庫(kù)實(shí)現(xiàn)文件MySquare.cpp,把上面的函數(shù)實(shí)現(xiàn)一遍。內(nèi)容為:

double mySquare(double inputValue) 
{
    return inputValue * inputValue;
}

就這樣數(shù)學(xué)庫(kù)的代碼寫(xiě)好了。接下來(lái)就是怎么把這個(gè)組織為一個(gè)庫(kù)的呢?

CMake組織庫(kù)

需要用上CMake的add_library方法。
add_library作用是用指定文件給工程添加一個(gè)庫(kù)。包括如下幾種:

  • 普通庫(kù)
  • 對(duì)象庫(kù)
  • 接口庫(kù)
  • 導(dǎo)入庫(kù)
  • 別名庫(kù)

其參數(shù)如下:

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

本文中先介紹普通庫(kù)。其中name屬性必須全局唯一。生成的library名會(huì)根據(jù)STATICSHARED成為name.a或name.so。
這里的STATICSHARED可不設(shè)置,通過(guò)全局的 BUILD_SHARED_LIBSFALSETRUE 來(lái)指定。最后就是源代碼目錄了。所以此CMake文件內(nèi)容為:

add_library(MathFunctions MySquare.cpp)

編寫(xiě)執(zhí)行代碼

庫(kù)寫(xiě)好之后接下來(lái)就是把它引到咱們的可執(zhí)行文件中。需要借助于CMake的add_subdirectory函數(shù)。其作用為為工程添加一個(gè)子目錄去編譯。add_subdirectory指定源文件和源CMakeLists.txt文件的目錄。所以根目錄的CMakeLists.txt文件內(nèi)容為:

cmake_minimum_required(VERSION 3.10)
project(Tutorial)
add_subdirectory(MathFunctions)
add_executable(Tutorial Tutorial.cpp)
#函數(shù)為把庫(kù)連接到可執(zhí)行文件中
target_link_libraries(Tutorial PUBLIC MathFunctions)
#指定頭文件搜索目錄
target_include_directories(Tutorial PUBLIC MathFunctions)

這兩個(gè)函數(shù)后面文章中專(zhuān)門(mén)拿出篇幅來(lái)講,暫時(shí)了解一下作用就行。

接下來(lái)寫(xiě)可執(zhí)行文件代碼,引相關(guān)頭文件來(lái)使用它來(lái)驗(yàn)證了??上攵鋬?nèi)容為:

#include <iostream>
#include <MathFunctions.h>

int main() {
    double inputValue = 5.0;
    double outputValue = mySquare(inputValue);
    std::cout << "result: " << outputValue << std::endl;
    return 0;
}

構(gòu)建和編譯項(xiàng)目:

> cmake ..
> make
> ./Tutorial

執(zhí)行結(jié)果為:

> result: 25

符合項(xiàng)目預(yù)期。

如何讓這個(gè)庫(kù)變成可選?

此時(shí)需要了解一下cmake命令option命令。并為根目錄的cmake添加:

option(USE_MYMATH "Use code provided math implementation" ON)

然后按照這個(gè)選項(xiàng)的值來(lái)讓編譯器編譯連接此庫(kù)。為此我們把根目錄的CMakelists.txt改造為。

if (USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions)
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

用if檢查選項(xiàng)值,在if塊內(nèi)用add_subdirectory把子目錄添加進(jìn)來(lái)。

用list命令來(lái)在列表中保存需要連接的庫(kù)和需要導(dǎo)入的頭文件。cmake中的list時(shí)以;分割的字符串。

list(APPEND <list> [<element> ...])
  • APPEND為修改命令,為list中添加元素。
  • <list>為list的變量名,如果當(dāng)前作用域中不存在這個(gè)變量名則作為空l(shuí)ist為其添加
  • [<element> ...]為list添加的元素

ps:也可以用set來(lái)創(chuàng)建list,例如:set(var a b c d) var的值為a;b;c;d;然后照樣可以用list的命令來(lái)處理var

之后在詳細(xì)介紹此兩個(gè)命令。目前簡(jiǎn)單理解就行。并且這種動(dòng)態(tài)控制的方法是較為常見(jiàn)的形式。

add_executable(Tutorial Tutorial.cpp)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
  • target_link_libraries是把我們的庫(kù)連接到可執(zhí)行文件
  • target_include_directories是為可執(zhí)行文件Tutorial添加頭文件搜索目錄,否則會(huì)找不到我們的頭文件

對(duì)源碼也做同樣的改造,用宏USE_MYMATH來(lái)決定用哪一個(gè)平方函數(shù):

#include <iostream>
#include <TutorialConfig.h>

#ifdef USE_MYMATH
#include "MathFunctions.h"
#else
#include <cmath>
#endif

int main() 
{
    double inputValue = 5.0;
#ifdef USE_MYMATH
    double outputValue = mySquare(inputValue);
    std::cout << "mySquare func called!" << std::endl;
#else
    double outputValue = pow(inputValue, 2);
    std::cout << "pow func called!" << std::endl;
#endif
    std::cout << "result: " << outputValue << std::endl;
    return 0;
}

由于我們?cè)谠创a中用到了USE_MYMATH,所以我們可以在TutorialConfig.h中添加#cmakedefine USE_MYMATH來(lái)讓CMake為我們定義USE_MYMATH宏。
configure_file之前介紹過(guò)是復(fù)制一份到指定目錄,并替換里面的變量。 #cmakedefine var是如果var在cmake中有設(shè)定會(huì)被替換為#define VAR否則/* #undef VAR */相當(dāng)于什么都不做。

ps:當(dāng)然也可以不用TutorialConfig.h這種形式來(lái)讓cmake為我們定義USE_MYMATH,直接add_definitions(-DUSE_MYMATH)來(lái)為源碼庫(kù)編譯添加一個(gè)宏

接下來(lái)就編譯看看是否符合預(yù)期。進(jìn)入build目錄

cmake .. -DUSE_MYMATH=OFF
或者
cmake .. -DUSE_MYMATH=ON
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容