CMake教程(二)

安裝和測試(步驟4)

現在,我們可以開始向項目添加安裝規則和測試支持。

安裝規則
安裝規則非常簡單:對於MathFunctions,我們要安裝庫和頭文件,對於應用程序,我們要安裝可執行文件和配置的頭文件。

因此,我們在MathFunctions/CMakeLists.txt最後添加:

install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

並在頂層CMakeLists.txt末尾添加:

install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  DESTINATION include
  )

運行cmake命令,然後選擇你的構建工具進行構建。使用以下install選項運行安裝步驟

make install

測試支持
接下來讓我們測試我們的應用程序。在頂級 CMakeLists.txt 文件的末尾,我們可以啓用測試,然後添加一些基本測試以驗證應用程序是否正常運行。

enable_testing()

# does the application run
add_test(NAME Runs COMMAND Tutorial 25)

# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# define a function to simplify adding tests
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction(do_test)

# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is [-nan|nan|0]")
do_test(Tutorial 0.0001 "0.0001 is 0.01")

第一個測試只是驗證應用程序正在運行,沒有段錯誤或其他崩潰,並且返回值爲零。這是CTest測試的基本形式。

下一個測試利用 PASS_REGULAR_EXPRESSION 測試屬性,以驗證測試的輸出包含某些字符串。在這種情況下,請驗證在提供了錯誤數量的參數時是否打印了錯誤消息。

最後,我們有一個名爲do_test的函數,用於運行應用程序並驗證所計算的平方根對於給定輸入是否正確。對於每次調用do_test,都會根據傳遞的參數將另一個測試連同名稱,輸入和預期結果一起添加到項目中。

重建應用程序,然後CD到二進制目錄並運行ctest可執行文件:

ctest -Nctest -VVctest -C Debug -VVRUN_TESTS

添加系統自省(步驟5)

讓我們考慮將一些代碼添加到我們的項目中,這取決於目標平臺可能不具備的功能。在此示例中,我們將添加一些代碼,具體取決於目標平臺是否具有logexp 功能。當然,幾乎每個平臺都具有這些功能,但對於本教程而言,它們並不常見。

我們將在TutorialConfig.h.in中使用新的定義,因此請確保在配置該文件之前進行設置。

include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
  unset(HAVE_LOG CACHE)
  unset(HAVE_EXP CACHE)
  set(CMAKE_REQUIRED_LIBRARIES "m")
  check_symbol_exists(log "math.h" HAVE_LOG)
  check_symbol_exists(exp "math.h" HAVE_EXP)
  if(HAVE_LOG AND HAVE_EXP)
    target_link_libraries(MathFunctions PRIVATE m)
  endif()
endif()

現在,將這些定義添加到TutorialConfig.h.in中以便我們可以從mysqrt.cxx中使用它們:

// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

如果logexp在系統上可用,那麼我們將使用它們來計算函數中的平方根mysqrt。將以下代碼添加到MathFunctions/mysqrt.cxx中的mysqrt函數中

#if defined(HAVE_LOG) && defined(HAVE_EXP)
  double result = exp(log(x) * 0.5);
  std::cout << "Computing sqrt of " << x << " to be " << result
            << " using log and exp" << std::endl;
#else
  double result = x;

我們還需要修改mysqrt.cxx以包含cmath

#include <cmath>

運行cmake命令,然後選擇你的構建工具進行構建,並運行Tutorial可執行文件。

您會注意到我們沒有使用logexp,即使我們認爲它們應該可用。我們應該很快認識到,我們都忘記了在mysqrt.cxx包括TutorialConfig.h

我們還需要更新MathFunctions/CMakeLists.txt,以便mysqrt.cxx 知道此文件的位置:

target_include_directories(MathFunctions
          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
          PRIVATE ${CMAKE_BINARY_DIR}
          )

完成此更新後,繼續並再次構建項目,然後運行構建的Tutorial可執行文件。如果logexp仍未使用,請從構建目錄打開生成的TutorialConfig.h文件。也許它們在當前系統上不可用?

指定編譯定義
除了in之外,還有更好的地方供我們保存HAVE_LOGHAVE_EXPTutorialConfig.h嗎?讓我們嘗試使用 target_compile_definitions()

首先,從TutorialConfig.h.in中刪除定義。

接下來,我們可以將check HAVE_LOGHAVE_EXP移至 MathFunctions/CMakeLists.txt,然後將這些值指定爲PRIVATE編譯定義。

include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
  unset(HAVE_LOG CACHE)
  unset(HAVE_EXP CACHE)
  set(CMAKE_REQUIRED_LIBRARIES "m")
  check_symbol_exists(log "math.h" HAVE_LOG)
  check_symbol_exists(exp "math.h" HAVE_EXP)
  if(HAVE_LOG AND HAVE_EXP)
    target_link_libraries(MathFunctions PRIVATE m)
  endif()
endif()

# add compile definitions
if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()

完成這些更新後,繼續並重新構建項目。運行內置的Tutorial可執行文件,並驗證結果與本步驟前面的內容相同。

添加自定義命令和生成的文件(步驟6)

假設,出於本教程的目的,我們決定永遠不想使用平臺logexp函數,而是想生成一個在mysqrt函數中使用的預計算值表。在本節中,我們將在構建過程中創建表,然後將該表編譯到我們的應用程序中。

首先,讓我們刪除MathFunctions/CMakeLists.txt中對logexp功能 的檢查。然後從mysqrt.cxx取出HAVE_LOGHAVE_EXP。同時,我們可以刪除 #include <cmath>

MathFunctions子目錄中,提供了一個名爲MakeTable.cxx的新源文件 來生成表。

查看完文件後,我們可以看到該表是作爲有效的C++代碼生成的,並且輸出文件名作爲參數傳入。

下一步是將適當的命令添加到 MathFunctions/CMakeLists.txt文件中,以構建MakeTable可執行文件,然後在構建過程中運行它。需要一些命令來完成此操作。

首先,在頂部MathFunctions/CMakeLists.txt,添加MakeTable的可執行文件, 就像添加任何其他可執行文件一樣。

add_executable(MakeTable MakeTable.cxx)

然後,我們添加一個自定義命令,該命令指定如何通過運行MakeTable 進行生產 Table.h

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )

接下來,我們必須讓CMake知道mysqrt.cxx取決於生成的文件Table.h。這是通過將生成的Table.h添加到庫MathFunctions的源列表中來完成的。

add_library(MathFunctions
            mysqrt.cxx
            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
            )

我們還必須將當前的二進制目錄添加到include目錄的列表中,以便mysqrt.cxx可以找到和包含Table.h

target_include_directories(MathFunctions
          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
          )

現在讓我們使用生成的表。首先爲Table.h修改mysqrt.cxx。接下來,我們可以重寫mysqrt函數以使用該表:

double mysqrt(double x)
{
  if (x <= 0) {
    return 0;
  }

  // use the table to help find an initial value
  double result = x;
  if (x >= 1 && x < 10) {
    std::cout << "Use the table to help find an initial value " << std::endl;
    result = sqrtTable[static_cast<int>(x)];
  }

  // do ten iterations
  for (int i = 0; i < 10; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
  }

  return result;
}

完成這些更新後,使用cmake重新構建項目。構建此項目時,將首先構建MakeTable可執行文件。然後將運行MakeTable產生Table.h。最後,它將編譯mysqrt.cxx,包括Table.h生成MathFunctions庫。

運行Tutorial可執行文件,並驗證它是否正在使用該表。

構建安裝程序(步驟7)

接下來,假設我們想將項目分發給其他人,以便他們可以使用它。我們希望在各種平臺上提供二進制和源代碼分發。這與我們之前在“ 安裝和測試”(第4步)中所做的安裝有些不同,在安裝和測試中,我們安裝根據源代碼構建的二進制文件。在此示例中,我們將構建支持二進制安裝和程序包管理功能的安裝程序包。爲此,我們將使用CPack創建平臺特定的安裝程序。具體來說,我們需要在頂級CMakeLists.txt文件的底部添加幾行。

include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include(CPack)

這就是全部。我們首先包括 InstallRequiredSystemLibraries。該模塊將包含項目當前平臺所需的任何運行時庫。接下來,我們將一些CPack變量設置爲存儲該項目的許可證和版本信息的位置。版本信息是在本教程的前面設置的,並且license.txt已包含在此步驟的頂級源目錄中。

最後,我們將 CPack module 它將使用這些變量和當前系統的其他一些屬性來設置安裝程序。

下一步是按通常的方式構建項目,然後運行 cpack可執行文件。要構建二進制發行版,請從二進制目錄運行:

cpack

要指定生成器,請使用-G選項。對於多配置版本,用於 -C指定配置。例如:

cpack -G ZIP -C Debug

要創建源分發,您可以輸入:

cpack --config CPackSourceConfig.cmake

運行在二進制目錄中找到的安裝程序。然後運行已安裝的可執行文件,並驗證其是否有效。

添加對儀表盤的支持(步驟8)

添加將測試結果提交到儀表板的支持非常簡單。我們已經在“ 測試支持”中爲我們的項目定義了許多測試。現在,我們只需要運行這些測試並將其提交到儀表板即可。爲了包括對儀表板的支持,我們包括了CTest頂層模塊 CMakeLists.txt

替換:

# enable testing
enable_testing()

爲:

# enable dashboard scripting
include(CTest)

CTest模塊將自動調用enable_testing(),因此我們可以將其從CMake文件中刪除。

我們還需要在頂級目錄中創建一個文件CTestConfig.cmake,在其中可以指定項目的名稱以及提交儀表板的位置。

set(CTEST_PROJECT_NAME "CMakeTutorial")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")

set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
set(CTEST_DROP_SITE_CDASH TRUE

)

ctest可執行文件將在運行時讀入此文件。要創建一個簡單的儀表板,您可以運行cmake 可執行文件,但尚未構建。進入二進制代碼目錄,然後運行:

ctest [-VV] -D Experimental

請記住,對於多配置生成器(例如Visual Studio),必須指定配置類型:

ctest [-VV] -C Debug -D Experimental

ctest可執行文件將構建並測試項目,並將結果提交到Kitware的公共儀表板:https://my.cdash.org/index.php?project=CMakeTutorial.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章