CMake
是一種跨平臺(tái)的免費(fèi)開源軟件工具铣鹏,用于使用與編譯器無關(guān)的方法來管理軟件的構(gòu)建過程。在 Android Studio
上進(jìn)行 NDK
開發(fā)默認(rèn)就是使用 CMake
管理 C/C++
代碼哀蘑,因此在學(xué)習(xí) NDK
之前最好對(duì) CMake
有一定的了解诚卸。
本文主要以翻譯 CMake
的官方教程文檔為主,加上自己的一些理解绘迁,該教程涵蓋了 CMake
的常見使用場(chǎng)景合溺。由于能力有限,翻譯部分采用機(jī)翻+人工校對(duì)缀台,翻譯有問題的地方棠赛,說聲抱歉。
開發(fā)環(huán)境:
- macOS 10.14.6
- CMake 3.15.1
- CLion 2018.2.4
添加“庫”的使用要求
使用要求可以更好地控制庫或可執(zhí)行文件的鏈接和包含行膛腐,同時(shí)還可以更好地控制 CMake
內(nèi)部目標(biāo)的傳遞屬性恭朗。利用使用要求的主要命令是:
-
target_compile_definitions
給指定目標(biāo)添加編譯定義。
-
target_compile_options
給指定目標(biāo)添加編譯選項(xiàng)依疼。
-
target_include_directories
給指定目標(biāo)添加包含目錄痰腮。
-
target_link_libraries
指定鏈接給定目標(biāo)或其依賴項(xiàng)時(shí)要使用的庫或標(biāo)志。
控制 CMake
內(nèi)部目標(biāo)的傳遞屬性有三種類型:
-
PRIVATE
屬性只應(yīng)用到本目標(biāo)律罢,不應(yīng)用到鏈接本目標(biāo)的目標(biāo)膀值。即生產(chǎn)者需要,消費(fèi)者不需要误辑。
-
PUBLIC
屬性既應(yīng)用到本目標(biāo)也應(yīng)用到鏈接目標(biāo)的目標(biāo)沧踏。即生產(chǎn)者和消費(fèi)者都需要。
-
INTERFACE
屬性不應(yīng)用到本目標(biāo)巾钉,應(yīng)用到鏈接本目標(biāo)的目標(biāo)翘狱。即生產(chǎn)者不需要,消費(fèi)者需要砰苍。
讓我們重構(gòu)代碼“提供選項(xiàng)”項(xiàng)目的代碼潦匈,以使用現(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
的使用要求裕膀,我們可以安全地從頂級(jí) CMakeLists.txt
中刪除對(duì) EXTRA_INCLUDES
變量的使用:
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
在項(xiàng)目根目錄運(yùn)行命令編譯項(xiàng)目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項(xiàng)目根目錄運(yùn)行生成的可執(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ī)則非常簡單:對(duì)于 MathFunctions
,我們要安裝庫和頭文件勇哗,對(duì)于應(yīng)用程序魂角,我們要安裝可執(zhí)行文件和配置的頭文件。
因此智绸,在 MathFunctions/CMakeLists.txt
的末尾添加:
# install rules
# 安裝規(guī)則
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
并在頂級(jí) CMakeLists.txt
的末尾添加:
# add the install targets
# 添加安裝規(guī)則
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
這就是本地安裝所需的全部。
在項(xiàng)目根目錄運(yùn)行命令編譯項(xiàng)目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項(xiàng)目根目錄運(yùn)行命令安裝可執(zhí)行文件:
cmake --install cmake-build-debug
CMake
從3.15開始使用cmake --install
安裝文件访忿。CMake
變量CMAKE_INSTALL_PREFIX
用于確定文件的安裝根目錄瞧栗。如果使用cmake --install
,則可以通過--prefix
參數(shù)指定自定義安裝目錄海铆。對(duì)于多配置工具迹恐,請(qǐng)使用--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
在項(xiàng)目根目錄執(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
這個(gè)時(shí)候我們調(diào)用的不是 cmake-build-debug
下的 Tutorial
文件卧斟,而是安裝到 /usr/local/bin
目錄下的 Tutorial
文件殴边。我們可以通過命令查看一下 Tutorial
的位置:
where Tutorial
終端輸出:
/usr/local/bin/Tutorial
測(cè)試
接下來,測(cè)試我們的應(yīng)用程序珍语。在頂級(jí) CMakeLists
文件的末尾锤岸,我們可以啟用測(cè)試,然后添加一些基本測(cè)試以驗(yàn)證應(yīng)用程序是否正常運(yùn)行板乙。
# enable testing
# 啟用測(cè)試
enable_testing()
# does the application run
# 測(cè)試應(yīng)用程序是否運(yùn)行
add_test(NAME Runs COMMAND Tutorial 25)
# does the usage message work?
# 測(cè)試消息是否工作是偷?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# define a function to simplify adding tests
# 定義一個(gè)函數(shù)以簡化添加測(cè)試
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é)果的測(cè)試
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")
第一個(gè)測(cè)試只是驗(yàn)證應(yīng)用程序正在運(yùn)行,沒有段錯(cuò)誤或其他崩潰募逞,并且返回值為零蛋铆。這是 CTest
測(cè)試的基本形式。
下一個(gè)測(cè)試使用 PASS_REGULAR_EXPRESSION
測(cè)試屬性來驗(yàn)證測(cè)試的輸出是否包含某些字符串放接。在這種情況下刺啦,驗(yàn)證在提供了錯(cuò)誤數(shù)量的參數(shù)時(shí)是否打印了用法消息。
最后纠脾,我們有一個(gè)名為 do_test
的函數(shù)玛瘸,該函數(shù)運(yùn)行應(yīng)用程序并驗(yàn)證所計(jì)算的平方根對(duì)于給定輸入是否正確。對(duì)于 do_test
的每次調(diào)用苟蹈,都會(huì)基于傳遞的參數(shù)將另一個(gè)測(cè)試添加到項(xiàng)目中捧韵,該測(cè)試具有名稱,輸入和預(yù)期結(jié)果汉操。
在項(xiàng)目根目錄運(yùn)行命令編譯項(xiàng)目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項(xiàng)目根目錄運(yùn)行命令測(cè)試應(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)自檢
讓我們考慮向我們的項(xiàng)目中添加一些代碼再来,這些代碼取決于目標(biāo)平臺(tái)可能不具備的功能。
對(duì)于此示例,我們將添加一些代碼芒篷,具體取決于目標(biāo)平臺(tái)是否具有 log
和 exp
函數(shù)搜变。當(dāng)然,幾乎每個(gè)平臺(tái)都具有這些功能针炉,但對(duì)于本教程而言挠他,假定它們并不常見。
如果平臺(tái)具有 log
和 exp
篡帕,那么我們將使用它們來計(jì)算 mysqrt
函數(shù)中的平方根殖侵。我們首先在頂級(jí) CMakeList
中使用 CheckSymbolExists.cmake
宏測(cè)試這些功能的可用性。
# 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
命令之前完成對(duì) log
和 exp
的測(cè)試非常重要拢军,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?
// 平臺(tái)是否提供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
的替代實(shí)現(xiàn)(在返回結(jié)果前不要忘記 #endif
!):
我們將在 TutorialConfig.h.in
中使用新定義蜡感,因此請(qǐng)確保在配置該文件之前進(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
在項(xiàng)目根目錄運(yùn)行命令編譯項(xiàng)目和生成可執(zhí)行文件:
cmake -B cmake-build-debug
cmake --build cmake-build-debug
在項(xiàng)目根目錄運(yùn)行生成的可執(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ǔ)項(xiàng)目
- 添加版本號(hào)和配置頭文件
- 指定C++標(biāo)準(zhǔn)
- 添加庫
- 提供選項(xiàng)
-
CMake使用教程(二)
- 添加“庫”的使用要求
- 安裝
- 測(cè)試
- 系統(tǒng)自檢
-
CMake使用教程(三)
- 指定編譯定義
- 添加自定義命令和生成的文件
- 生成安裝程序
- 添加對(duì)儀表板的支持
-
CMake使用教程(四)
- 混合靜態(tài)和共享
- 添加生成器表達(dá)式
- 添加導(dǎo)出配置