在之前的基礎(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ù)STATIC或SHARED成為name.a或name.so。
這里的STATIC和SHARED可不設(shè)置,通過(guò)全局的 BUILD_SHARED_LIBS 的 FALSE 或 TRUE 來(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