CMake使用教程(二)

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

如果平臺具有 logexp ,那么我們將使用它們來計算 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.hconfigure_file 命令之前完成對 logexp 的測試非常重要,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 以包含 cmathTutorialConfig.h。接下來,在 mysqrt函數(shù)的同一文件中,我們可以使用以下代碼(如果在系統(tǒng)上可用)提供基于 logexp 的替代實現(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)出配置
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容