CMake使用教程(二)

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)是否具有 logexp 函數(shù)搜变。當(dāng)然,幾乎每個(gè)平臺(tái)都具有這些功能针炉,但對(duì)于本教程而言挠他,假定它們并不常見。

如果平臺(tái)具有 logexp 篡帕,那么我們將使用它們來計(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.hconfigure_file 命令之前完成對(duì) logexp 的測(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 以包含 cmathTutorialConfig.h。接下來献幔,在 mysqrt函數(shù)的同一文件中懂傀,我們可以使用以下代碼(如果在系統(tǒng)上可用)提供基于 logexp 的替代實(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)出配置
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铸敏,隨后出現(xiàn)的幾起案子缚忧,更是在濱河造成了極大的恐慌,老刑警劉巖杈笔,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闪水,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒙具,警方通過查閱死者的電腦和手機(jī)球榆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禁筏,“玉大人持钉,你說我怎么就攤上這事±槲簦” “怎么了每强?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵始腾,是天一觀的道長。 經(jīng)常有香客問我空执,道長浪箭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任辨绊,我火速辦了婚禮奶栖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘门坷。我一直安慰自己宣鄙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布默蚌。 她就那樣靜靜地躺著冻晤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敏簿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天宣虾,我揣著相機(jī)與錄音惯裕,去河邊找鬼。 笑死绣硝,一個(gè)胖子當(dāng)著我的面吹牛蜻势,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纯命,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼需曾,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼篮迎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挠铲,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寂诱,沒想到半個(gè)月后拂苹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痰洒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瓢棒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丘喻。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脯宿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泉粉,到底是詐尸還是另有隱情连霉,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窘面,受9級(jí)特大地震影響翠语,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜财边,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一肌括、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酣难,春花似錦谍夭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菜谣,卻和暖如春珠漂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尾膊。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工媳危, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冈敛。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓待笑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抓谴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暮蹂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355