步驟 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
  const double outputValue = mathfunctions::sqrt(inputValue);

練習 2 - 新增選項

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

CMake 可以使用 option() 命令來做到這一點。這會給使用者一個變數,讓他們在設定 CMake 建置時可以變更。此設定會儲存在快取中,這樣使用者每次在建置目錄上執行 CMake 時就不需要設定值。

目標

新增一個選項,讓建置時不包含 MathFunctions

實用資源

要編輯的檔案

  • MathFunctions/CMakeLists.txt

  • MathFunctions/MathFunctions.cxx

開始入門

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

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

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

最後,當 USE_MYMATH 開啟時,透過在 MathFunctions/CMakeLists.txtUSE_MYMATH 區塊內將 mysqrt.cxx 設為自己的函式庫,以防止編譯 mysqrt.cxx

建置與執行

由於我們已經從練習 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 來操作在建置中使用的函式庫。