CMake 是一種跨平臺的免費開源軟件工具,用于使用與編譯器無關(guān)的方法來管理軟件的構(gòu)建過程。在 Android Studio 上進(jìn)行 NDK 開發(fā)默認(rèn)就是使用 CMake 管理 C/C++ 代碼,因此在學(xué)習(xí) NDK 之前最好對 CMake 有一定的了解。
本文主要以翻譯 CMake 的官方教程文檔為主,加上自己的一些理解,該教程涵蓋了 CMake 的常見使用場景。由于能力有限,翻譯部分采用機翻+人工校對,翻譯有問題的地方,說聲抱歉。
開發(fā)環(huán)境:
- macOS 10.14.6
- CMake 3.15.1
- CLion 2018.2.4
添加“庫”的使用要求
使用要求可以更好地控制庫或可執(zhí)行文件的鏈接和包含行,同時還可以更好地控制 CMake 內(nèi)部目標(biāo)的傳遞屬性。利用使用要求的主要命令是:
-
target_compile_definitions給指定目標(biāo)添加編譯定義。
-
target_compile_options給指定目標(biāo)添加編譯選項。
-
target_include_directories給指定目標(biāo)添加包含目錄。
-
target_link_libraries指定鏈接給定目標(biāo)或其依賴項時要使用的庫或標(biāo)志。
控制 CMake 內(nèi)部目標(biāo)的傳遞屬性有三種類型:
-
PRIVATE屬性只應(yīng)用到本目標(biāo),不應(yīng)用到鏈接本目標(biāo)的目標(biāo)。即生產(chǎn)者需要,消費者不需要。
-
PUBLIC屬性既應(yīng)用到本目標(biāo)也應(yīng)用到鏈接目標(biāo)的目標(biāo)。即生產(chǎn)者和消費者都需要。
-
INTERFACE屬性不應(yīng)用到本目標(biāo),應(yīng)用到鏈接本目標(biāo)的目標(biāo)。即生產(chǎn)者不需要,消費者需要。
讓我們重構(gòu)代碼“提供選項”項目的代碼,以使用現(xiàn)代 CMake 的使用要求方法。我們首先聲明,鏈接到 MathFunctions 的任何人都需要包含當(dāng)前源目錄,而 MathFunctions 本身不需要。因此,這里使用 INTERFACE。
將以下行添加到 MathFunctions/CMakeLists.txt 的末尾:
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
# 說明與我們鏈接的任何人都需要包含當(dāng)前源目錄才能找到 MathFunctions.h,而我們不需要。
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
現(xiàn)在,我們已經(jīng)指定了 MathFunction 的使用要求,我們可以安全地從頂級 CMakeLists.txt 中刪除對 EXTRA_INCLUDES變量的使用:
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
在項目根目錄運行命令編譯項目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項目根目錄運行生成的可執(zhí)行文件:
./cmake-build-debug/Tutorial 2
終端輸出:
Computing sqrt of 2 to be 1.5
Computing sqrt of 2 to be 1.41667
Computing sqrt of 2 to be 1.41422
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
The square root of 2 is 1.41421
安裝
安裝規(guī)則非常簡單:對于 MathFunctions ,我們要安裝庫和頭文件,對于應(yīng)用程序,我們要安裝可執(zhí)行文件和配置的頭文件。
因此,在 MathFunctions/CMakeLists.txt 的末尾添加:
# install rules
# 安裝規(guī)則
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
并在頂級 CMakeLists.txt 的末尾添加:
# add the install targets
# 添加安裝規(guī)則
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
這就是本地安裝所需的全部。
在項目根目錄運行命令編譯項目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項目根目錄運行命令安裝可執(zhí)行文件:
cmake --install cmake-build-debug
CMake從3.15開始使用cmake --install安裝文件。CMake變量CMAKE_INSTALL_PREFIX用于確定文件的安裝根目錄。如果使用cmake --install,則可以通過--prefix參數(shù)指定自定義安裝目錄。對于多配置工具,請使用--config參數(shù)指定配置。
終端輸出:
-- Install configuration: ""
-- Installing: /usr/local/lib/libMathFunctions.a
-- Installing: /usr/local/include/MathFunctions.h
-- Installing: /usr/local/bin/Tutorial
-- Installing: /usr/local/include/TutorialConfig.h
在項目根目錄執(zhí)行命令:
Tutorial 2
終端輸出:
Computing sqrt of 2 to be 1.5
Computing sqrt of 2 to be 1.41667
Computing sqrt of 2 to be 1.41422
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
Computing sqrt of 2 to be 1.41421
The square root of 2 is 1.41421
這個時候我們調(diào)用的不是 cmake-build-debug 下的 Tutorial 文件,而是安裝到 /usr/local/bin 目錄下的 Tutorial 文件。我們可以通過命令查看一下 Tutorial 的位置:
where Tutorial
終端輸出:
/usr/local/bin/Tutorial
測試
接下來,測試我們的應(yīng)用程序。在頂級 CMakeLists 文件的末尾,我們可以啟用測試,然后添加一些基本測試以驗證應(yīng)用程序是否正常運行。
# enable testing
# 啟用測試
enable_testing()
# does the application run
# 測試應(yīng)用程序是否運行
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
# 定義一個函數(shù)以簡化添加測試
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
# 做一堆基于結(jié)果的測試
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")
第一個測試只是驗證應(yīng)用程序正在運行,沒有段錯誤或其他崩潰,并且返回值為零。這是 CTest 測試的基本形式。
下一個測試使用 PASS_REGULAR_EXPRESSION 測試屬性來驗證測試的輸出是否包含某些字符串。在這種情況下,驗證在提供了錯誤數(shù)量的參數(shù)時是否打印了用法消息。
最后,我們有一個名為 do_test 的函數(shù),該函數(shù)運行應(yīng)用程序并驗證所計算的平方根對于給定輸入是否正確。對于 do_test 的每次調(diào)用,都會基于傳遞的參數(shù)將另一個測試添加到項目中,該測試具有名稱,輸入和預(yù)期結(jié)果。
在項目根目錄運行命令編譯項目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項目根目錄運行命令測試應(yīng)用程序:
cd cmake-build-debug
ctest
終端輸出:
Test project /Users/taylor/Project/Taylor/C/Study/cmake-tutorial/cmake-test/cmake-build-debug
Start 1: Runs
1/9 Test #1: Runs ............................. Passed 0.00 sec
Start 2: Usage
2/9 Test #2: Usage ............................ Passed 0.00 sec
Start 3: Comp4
3/9 Test #3: Comp4 ............................ Passed 0.00 sec
Start 4: Comp9
4/9 Test #4: Comp9 ............................ Passed 0.00 sec
Start 5: Comp5
5/9 Test #5: Comp5 ............................ Passed 0.00 sec
Start 6: Comp7
6/9 Test #6: Comp7 ............................ Passed 0.00 sec
Start 7: Comp25
7/9 Test #7: Comp25 ........................... Passed 0.00 sec
Start 8: Comp-25
8/9 Test #8: Comp-25 .......................... Passed 0.00 sec
Start 9: Comp0.0001
9/9 Test #9: Comp0.0001 ....................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 9
Total Test time (real) = 0.03 sec
系統(tǒng)自檢
讓我們考慮向我們的項目中添加一些代碼,這些代碼取決于目標(biāo)平臺可能不具備的功能。
對于此示例,我們將添加一些代碼,具體取決于目標(biāo)平臺是否具有 log 和 exp 函數(shù)。當(dāng)然,幾乎每個平臺都具有這些功能,但對于本教程而言,假定它們并不常見。
如果平臺具有 log 和 exp ,那么我們將使用它們來計算 mysqrt 函數(shù)中的平方根。我們首先在頂級 CMakeList 中使用 CheckSymbolExists.cmake 宏測試這些功能的可用性。
# does this system provide the log and exp functions?
# 該系統(tǒng)是否提供log和exp函數(shù)?
include(CheckSymbolExists)
set(CMAKE_REQUIRED_LIBRARIES "m")
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
在 TutorialConfig.h 的 configure_file 命令之前完成對 log 和 exp 的測試非常重要,configure_file 命令使用 CMake 中的當(dāng)前設(shè)置立即配置文件,所以 check_symbol_exists 命令應(yīng)該放在 configure_file 之前。
現(xiàn)在,將這些定義添加到 TutorialConfig.h.in 中,以便我們可以從 mysqrt.cxx中使用它們:
// does the platform provide exp and log functions?
// 平臺是否提供log和exp函數(shù)?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
更新 MathFunctions/CMakeLists.txt 文件,以便 mysqrt.cxx知道此文件的位置:
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_BINARY_DIR}
)
修改 mysqrt.cxx 以包含 cmath 和 TutorialConfig.h。接下來,在 mysqrt函數(shù)的同一文件中,我們可以使用以下代碼(如果在系統(tǒng)上可用)提供基于 log 和 exp 的替代實現(xiàn)(在返回結(jié)果前不要忘記 #endif !):
我們將在 TutorialConfig.h.in 中使用新定義,因此請確保在配置該文件之前進(jìn)行設(shè)置。
#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;
// 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;
}
#endif
在項目根目錄運行命令編譯項目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項目根目錄運行生成的可執(zhí)行文件:
./cmake-build-debug/Tutorial 2
終端輸出:
Computing sqrt of 2 to be 1.41421 using log and exp
The square root of 2 is 1.41421
CMake使用教程系列文章
-
CMake使用教程(一)
- 基礎(chǔ)項目
- 添加版本號和配置頭文件
- 指定C++標(biāo)準(zhǔn)
- 添加庫
- 提供選項
-
CMake使用教程(二)
- 添加“庫”的使用要求
- 安裝
- 測試
- 系統(tǒng)自檢
-
CMake使用教程(三)
- 指定編譯定義
- 添加自定義命令和生成的文件
- 生成安裝程序
- 添加對儀表板的支持
-
CMake使用教程(四)
- 混合靜態(tài)和共享
- 添加生成器表達(dá)式
- 添加導(dǎo)出配置