步驟 2:新增函式庫

到目前為止,我們已經了解如何使用 CMake 建立基本專案。在此步驟中,我們將學習如何在專案中建立和使用函式庫。我們也將了解如何使函式庫的使用成為可選的。

練習 1 - 建立函式庫

若要在 CMake 中新增函式庫,請使用 add_library() 命令,並指定哪些原始碼檔案應組成該函式庫。

我們可以將專案組織成一個或多個子目錄,而不是將所有原始碼檔案放在一個目錄中。在本例中,我們將專門為函式庫建立一個子目錄。在這裡,我們可以新增一個新的 CMakeLists.txt 檔案和一個或多個原始碼檔案。在頂層的 CMakeLists.txt 檔案中,我們將使用 add_subdirectory() 命令將子目錄新增到建置中。

函式庫建立完成後,它會透過 target_include_directories()target_link_libraries() 連接到我們的可執行目標。

目標

新增和使用函式庫。

實用資源

要編輯的檔案

  • CMakeLists.txt

  • tutorial.cxx

  • MathFunctions/CMakeLists.txt

開始使用

在本練習中,我們將在專案中新增一個函式庫,其中包含我們自己用於計算數字平方根的實作。然後,可執行檔可以使用此函式庫,而不是編譯器提供的標準平方根函數。

在本教學中,我們將把函式庫放入名為 MathFunctions 的子目錄中。此目錄已包含標頭檔 MathFunctions.hmysqrt.h。它們各自的原始碼檔案 MathFunctions.cxxmysqrt.cxx 也已提供。我們不需要修改這些檔案中的任何一個。mysqrt.cxx 有一個名為 mysqrt 的函數,其功能與編譯器的 sqrt 函數類似。MathFunctions.cxx 包含一個函數 sqrt,用於隱藏 sqrt 的實作細節。

Help/guide/tutorial/Step2 目錄中,從 TODO 1 開始,完成到 TODO 6

首先,在 MathFunctions 子目錄中填寫單行 CMakeLists.txt

接下來,編輯頂層的 CMakeLists.txt

最後,在 tutorial.cxx 中使用新建立的 MathFunctions 函式庫

建置與執行

執行 cmake 可執行檔或 cmake-gui 來設定專案,然後使用您選擇的建置工具來建置它。

以下是從命令列執行的步驟複習

mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .

嘗試使用新建立的 Tutorial,並確保它仍然產生準確的平方根值。

解答

MathFunctions 目錄中的 CMakeLists.txt 檔案中,我們使用 add_library() 建立一個名為 MathFunctions 的函式庫目標。函式庫的原始碼檔案作為參數傳遞給 add_library()。這看起來像以下這行

TODO 1:點擊以顯示/隱藏答案
TODO 1:MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

為了使用新的函式庫,我們將在頂層的 CMakeLists.txt 檔案中新增一個 add_subdirectory() 呼叫,以便建置該函式庫。

TODO 2:點擊以顯示/隱藏答案
TODO 2:CMakeLists.txt
add_subdirectory(MathFunctions)

接下來,新的函式庫目標使用 target_link_libraries() 連結到可執行目標。

TODO 3:點擊以顯示/隱藏答案

最後,我們需要指定函式庫標頭檔的位置。修改現有的 target_include_directories() 呼叫,以新增 MathFunctions 子目錄作為包含目錄,以便可以找到 MathFunctions.h 標頭檔。

TODO 4:點擊以顯示/隱藏答案
TODO 4:CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

現在讓我們使用我們的函式庫。在 tutorial.cxx 中,包含 MathFunctions.h

TODO 5:點擊以顯示/隱藏答案
TODO 5:tutorial.cxx
#include "MathFunctions.h"

最後,將 sqrt 替換為包裝函式 mathfunctions::sqrt

TODO 6:點擊以顯示/隱藏答案
TODO 6:tutorial.cxx
  double const outputValue = mathfunctions::sqrt(inputValue);

練習 2 - 新增選項

現在讓我們在 MathFunctions 函式庫中新增一個選項,讓開發人員可以選擇自訂平方根實作或內建標準實作。雖然對於本教學來說,實際上沒有這樣做的必要,但對於較大型的專案來說,這是一種常見的情況。

CMake 可以使用 option() 命令來做到這一點。這為使用者提供了一個變數,他們可以在設定 cmake 建置時變更該變數。此設定將儲存在快取中,以便使用者在每次對建置目錄執行 CMake 時,都不需要設定該值。

目標

新增不建置 MathFunctions 的選項。

實用資源

要編輯的檔案

  • MathFunctions/CMakeLists.txt

  • MathFunctions/MathFunctions.cxx

開始使用

從練習 1 的結果檔案開始。完成 TODO 7TODO 14

首先,在 MathFunctions/CMakeLists.txt 中使用 option() 命令建立一個變數 USE_MYMATH。在同一個檔案中,使用該選項將編譯定義傳遞給 MathFunctions 函式庫。

然後,更新 MathFunctions.cxx 以根據 USE_MYMATH 重新導向編譯。

最後,當 USE_MYMATH 為啟用狀態時,透過將 mysqrt.cxx 放入 MathFunctions/CMakeLists.txtUSE_MYMATH 區塊內的其自身函式庫中,來防止編譯它。

建置與執行

由於我們已經從練習 1 設定了建置目錄,因此我們可以透過簡單地呼叫以下命令來重建

cd ../Step2_build
cmake --build .

接下來,在幾個數字上執行 Tutorial 可執行檔,以驗證它是否仍然正確。

現在讓我們將 USE_MYMATH 的值更新為 OFF。最簡單的方法是使用 cmake-guiccmake(如果您在終端機中)。或者,如果您想從命令列變更選項,請嘗試

cmake ../Step2 -DUSE_MYMATH=OFF

現在,使用以下命令重建程式碼

cmake --build .

然後,再次執行可執行檔,以確保在 USE_MYMATH 設定為 OFF 的情況下,它仍然可以運作。哪個函數能提供更好的結果,sqrt 還是 mysqrt

解答

第一步是在 MathFunctions/CMakeLists.txt 中新增一個選項。此選項將顯示在 cmake-guiccmake 中,預設值為 ON,使用者可以變更。

TODO 7:點擊以顯示/隱藏答案
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:點擊以顯示/隱藏答案
TODO 8:MathFunctions/CMakeLists.txt
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()

USE_MYMATHON 時,將設定編譯定義 USE_MYMATH。然後,我們可以使用此編譯定義來啟用或停用原始碼的某些區段。

對原始碼進行的相應變更相當簡單。在 MathFunctions.cxx 中,我們讓 USE_MYMATH 控制使用哪個平方根函數

TODO 9:點擊以顯示/隱藏答案
TODO 9:MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
  return detail::mysqrt(x);
#else
  return std::sqrt(x);
#endif

接下來,如果定義了 USE_MYMATH,我們需要包含 mysqrt.h

TODO 10:點擊以顯示/隱藏答案
TODO 10:MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
#  include "mysqrt.h"
#endif

最後,既然我們正在使用 std::sqrt,我們現在需要包含 cmath

TODO 11:點擊以顯示/隱藏答案
TODO 11 : MathFunctions/MathFunctions.cxx
#include <cmath>

此時,如果 USE_MYMATHOFF,則不會使用 mysqrt.cxx,但它仍會被編譯,因為 MathFunctions 目標在來源下已列出 mysqrt.cxx

有幾種方法可以解決這個問題。第一個選項是使用 target_sources()USE_MYMATH 區塊內新增 mysqrt.cxx。另一個選項是在 USE_MYMATH 區塊內建立一個額外的函式庫,負責編譯 mysqrt.cxx。為了本教學的目的,我們將建立一個額外的函式庫。

首先,從 USE_MYMATH 內部建立一個名為 SqrtLibrary 的函式庫,其來源為 mysqrt.cxx

TODO 12:點擊以顯示/隱藏答案
TODO 12 : MathFunctions/CMakeLists.txt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

  # TODO 6: Link SqrtLibrary to tutorial_compiler_flags

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

接下來,當啟用 USE_MYMATH 時,我們將 SqrtLibrary 連結到 MathFunctions

TODO 13:點擊以顯示/隱藏答案

最後,我們可以從 MathFunctions 函式庫來源清單中移除 mysqrt.cxx,因為當包含 SqrtLibrary 時,它將被拉入。

TODO 14:點擊以顯示/隱藏答案
TODO 14 : MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx)

透過這些變更,對於正在建置和使用 MathFunctions 函式庫的任何人來說,mysqrt 函數現在完全是可選的。使用者可以切換 USE_MYMATH 來操作建置中使用的函式庫。