??? 本節將學習如何在項目中創建和使用庫,還將看到如何使庫的使用成為可選的。
??? 本節中使用的示例代碼下載見step1-簡單開始cmake實踐-CSDN博客。
練習1 -創建一個庫
??? 要在CMake中添加一個庫,使用add_library()命令并指定哪些源文件應該組成該庫。
??? 我們可以使用一個或多個子目錄組織項目,而不是將所有源文件放在一個目錄中。在本例中,我們將專門為庫創建一個子目錄。在這里,我們可以添加一個新的CMakeLists.txt文件和一個或多個源文件。在頂級的CMakeLists.txt文件中,我們將使用add_subdirectory()命令將子目錄添加到構建中。
??? 創建庫之后,使用target_include_directories()和target_link_libraries()將其連接到可執行目標。
目標
??? 添加并使用庫。
開始
??? 在本練習中,我們將在項目中添加一個庫,其中包含我們自己的用于計算數字平方根的實現。然后,可執行文件可以使用這個庫,而不采用編譯器提供的標準平方根函數。
??? 在本教程中,我們將把庫放入名為MathFunctions的子目錄中。這個目錄已經包含了頭文件MathFunctions.h和mysqrt.h。還提供了它們各自的源文件MathFunctions.cxx和mysqrt.cxx。我們不需要修改這些文件。mysqrt.cxx有一個名為mysqrt的函數,它提供了與編譯器的SQRT函數類似的功能。MathFunctions.cxx包含一個函數SQRT,用于隱藏我們自己寫的SQRT函數的實現細節。
???? 從在本文提供的代碼實例文件夾下/Step2目錄中,從TODO 1開始,完成TODO 6。
??? 首先,在MathFunctions子目錄中填寫一行CMakeLists.txt。
??? 接下來,編輯頂層的CMakeLists.txt。
??? 最后,使用tutorial.cxx中新創建的MathFunctions庫。
操作
??? 在MathFunctions目錄下的CMakeLists.txt文件中,我們使用add_library()創建了一個名為MathFunctions的庫目標。庫的源文件作為參數傳遞給add_library()。這看起來像下面這行:
// TODO 1: MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
??? 為了使用新庫,我們將在頂級CMakeLists.txt文件中添加add_subdirectory()調用,以便庫將被構建。
// TODO 2: CMakeLists.txt
add_subdirectory(MathFunctions)
??? 接下來,使用target_link_libraries()將新的庫目標鏈接到可執行目標。
// TODO 3: CMakeLists.txttarget_link_libraries(Tutorial PUBLIC MathFunctions)
??? 最后,我們需要指定庫的頭文件位置。修改現有的target_include_directories()調用,將MathFunctions子目錄添加為包含目錄,這樣就可以找到MathFunctions.h頭文件。
// TODO 4: CMakeLists.txt
target_include_directories(Tutorial PUBLIC"${PROJECT_BINARY_DIR}""${PROJECT_SOURCE_DIR}/MathFunctions")
??? 現在讓我們使用庫。在tutorial.cxx
中包含MathFunctions.h:
// TODO 5: tutorial.cxx#include "MathFunctions.h"
??? 最后,用包裝函數mathfunctions::sqrt替換sqrt。
// TODO 6: tutorial.cxxconst double outputValue = mathfunctions::sqrt(inputValue);
編譯和運行
mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .
??? 嘗試使用編譯好的項目,并確保它仍然產生準確的平方根值。
./Tutorial 4294967296
./Tutorial 10
./Tutorial
練習2 -添加選項
??? 現在讓我們在MathFunctions庫中添加一個選項,允許開發人員選擇自定義的平方根實現或內置的標準實現。
??? CMake可以使用option()命令執行此操作。這為用戶提供了一個可以在配置cmake構建時更改的變量。
目標
??? 添加不使用MathFunctions函數的編譯選項。
步驟
???? 首先使用MathFunctions/CMakeLists.txt中的option()命令創建一個變量USE_MYMATH。在同一個文件中,使用該選項將編譯定義傳遞給MathFunctions庫。
??? 然后,更新MathFunctions,基于USE_MYMATH重定向編譯。
??? 最后,當USE_MYMATH打開時,通過在MathFunctions/CMakeLists.txt的USE_MYMATH塊中使其成為自己的庫。
操作
??? 第一步是在MathFunctions/CMakeLists.txt中添加一個選項,默認值為ON,可由用戶更改。
// TODO 7: MathFunctions/CMakeLists.txt
option(USE_MYMATH "Use tutorial provided math implementation" ON)
??? 接下來,使用這個新選項使庫與mysqrt函數的構建和鏈接成為有條件的選項。
??? 創建一個if()語句來檢查USE_MYMATH的值。在if()塊中,放入target_compile_definitions()命令和編譯定義USE_MYMATH。
// TODO 8: MathFunctions/CMakeLists.txt
if (USE_MYMATH)target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()
條件判斷:
-
if (USE_MYMATH)
這里使用了 CMake 的
if
命令來判斷變量USE_MYMATH
是否為真(即非空或者非零)。如果USE_MYMATH
被定義為ON
、TRUE
、非空字符串或者被設置為一個非零數值,條件判斷將會成立。 -
目標編譯定義:
-
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
如果
USE_MYMATH
的條件成立,那么就會為目標MathFunctions
添加一個編譯定義。在這里,編譯定義是"USE_MYMATH"
。這意味著在編譯MathFunctions
庫或可執行文件時,會將"USE_MYMATH"
添加到編譯器的預處理器定義中。 -
PRIVATE 關鍵字:
PRIVATE
關鍵字指定了這個編譯定義的作用域。在 CMake 中,PRIVATE
表示這個定義只會影響到MathFunctions
目標本身及其直接依賴的其他目標。這意味著其他使用MathFunctions
的目標或庫不會自動繼承"USE_MYMATH"
這個定義,除非它們也顯式地聲明。
??? 這段代碼的作用是根據 USE_MYMATH
變量的值來決定是否為 MathFunctions
添加一個編譯定義 "USE_MYMATH"
。這樣的做法通常用于根據不同的構建選項或條件,啟用或禁用特定的功能或代碼路徑。
??? 當USE_MYMATH為ON時,編譯定義USE_MYMATH將被設置。然后,我們可以使用這個編譯定義來啟用或禁用源代碼的部分。
??? 接下來在MathFunctions.cxx文件中,我們讓USE_MYMATH控制使用哪個平方根函數:
#ifdef USE_MYMATHreturn detail::mysqrt(x);
#elsereturn std::sqrt(x);
#endif
??? 接下來,如果定義了USE_MYMATH,我們需要包含mysqrt.h。
// TODO 10: MathFunctions/MathFunctions.cxx#ifdef USE_MYMATH
# include "mysqrt.h"
#endif
??? 最后,在使用std::sqrt時,我們需要包含cmath。
// TODO 11 : MathFunctions/MathFunctions.cxx#include <cmath>
??? 此時,如果USE_MYMATH為OFF, mysqrt.cxx不會被使用,但它仍然會被編譯,因為MathFunctions目標有mysqrt.cxx列在來源下面。
??? 有幾種方法可以解決這個問題。第一個選項是使用target_sources()來添加mysqrt.cxx從USE_MYMATH塊中取出。另一個選擇是在USE_MYMATH塊中創建一個額外的庫,它負責編譯mysqrt.cxx。在本教程中,我們將創建一個額外的庫。
??? 首先,在USE_MYMATH中創建一個名為SqrtLibrary的庫,其源代碼為mysqrt.cxx。
// TODO 12 : MathFunctions/CMakeLists.txtadd_library(SqrtLibrary STATICmysqrt.cxx)# TODO 6: Link SqrtLibrary to tutorial_compiler_flagstarget_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
??? 接下來,當啟用USE_MYMATH時,我們將SqrtLibrary鏈接到MathFunctions。
// TODO 13 : MathFunctions/CMakeLists.txttarget_link_libraries(MathFunctions PRIVATE SqrtLibrary)
??? 最后,我們可以從MathFunctions庫源列表中刪除mysqrt.cxxcxx,因為它將在包含SqrtLibrary時被拉入。
// TODO 14 : MathFunctions/CMakeLists.txtadd_library(MathFunctions MathFunctions.cxx)
??? 通過這些更改,mysqrt函數現在對于正在構建和使用MathFunctions庫的人來說完全是可選的。用戶可以切換USE_MYMATH來操作編譯中使用的庫。
編譯并運行
??? 由于我們已經在練習1中配置了編譯目錄,我們可以通過簡單地調用以下命令進行編譯:
cd ../Step2_build
cmake --build .
??? 現在讓我們將USE_MYMATH的值更新為OFF。
cmake ../Step2 -DUSE_MYMATH=OFF
??? 現在,用下面的代碼重新編譯代碼:
cmake --build .
??? 然后,再次運行可執行文件,以確保它在USE_MYMATH設置為OFF時仍然工作。