安裝和測試(步驟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)
讓我們考慮將一些代碼添加到我們的項目中,這取決於目標平臺可能不具備的功能。在此示例中,我們將添加一些代碼,具體取決於目標平臺是否具有log
和exp
功能。當然,幾乎每個平臺都具有這些功能,但對於本教程而言,它們並不常見。
我們將在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
如果log
和exp
在系統上可用,那麼我們將使用它們來計算函數中的平方根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
可執行文件。
您會注意到我們沒有使用log
和exp
,即使我們認爲它們應該可用。我們應該很快認識到,我們都忘記了在mysqrt.cxx
包括TutorialConfig.h
。
我們還需要更新MathFunctions/CMakeLists.txt
,以便mysqrt.cxx
知道此文件的位置:
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_BINARY_DIR}
)
完成此更新後,繼續並再次構建項目,然後運行構建的Tutorial
可執行文件。如果log
和exp
仍未使用,請從構建目錄打開生成的TutorialConfig.h
文件。也許它們在當前系統上不可用?
指定編譯定義
除了in之外,還有更好的地方供我們保存HAVE_LOG
和HAVE_EXP
值TutorialConfig.h
嗎?讓我們嘗試使用 target_compile_definitions()
。
首先,從TutorialConfig.h.in
中刪除定義。
接下來,我們可以將check HAVE_LOG
和HAVE_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)
假設,出於本教程的目的,我們決定永遠不想使用平臺log
和exp
函數,而是想生成一個在mysqrt
函數中使用的預計算值表。在本節中,我們將在構建過程中創建表,然後將該表編譯到我們的應用程序中。
首先,讓我們刪除MathFunctions/CMakeLists.txt
中對log
和exp
功能 的檢查。然後從mysqrt.cxx
取出HAVE_LOG
和 HAVE_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.