CMake使用教程(三)

CMake 是一種跨平臺的免費開源軟件工具,用於使用與編譯器無關的方法來管理軟件的構建過程。在 Android Studio 上進行 NDK 開發默認就是使用 CMake 管理 C/C++ 代碼,所以在學習 NDK 以前最好對 CMake 有必定的瞭解。php

本文主要以翻譯 CMake官方教程文檔爲主,加上本身的一些理解,該教程涵蓋了 CMake 的常見使用場景。因爲能力有限,翻譯部分採用機翻+人工校對,翻譯有問題的地方,說聲抱歉。html

開發環境:ios

  • macOS 10.14.6
  • CMake 3.15.1
  • CLion 2018.2.4

指定編譯定義

示例程序地址c++

在上一步 「系統自檢」 中,除了在 TutorialConfig.h 中保存 HAVE_LOGHAVE_EXP 值以外,還有更好的作法嗎?對於此示例,咱們將嘗試使用 target_compile_definitionsgit

首先,從 TutorialConfig.h.in 中刪除上一步的定義,在 mysqrt.cxx 中再也不包含 TutorialConfig.h,移除上一步在 MathFunctions/CMakeLists.txt 中增長的額外包含。github

接下來,咱們能夠將 HAVE_LOGHAVE_EXP 的檢查移至 MathFunctions/CMakeLists.txt,而後添加將這些值指定爲 PRIVATE 編譯定義。shell

# does this system provide the log and exp functions?
# 該系統是否提供log和exp函數?
include(CheckSymbolExists)
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_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
複製代碼

完成這些更新後,在項目根目錄運行命令編譯項目和生成可執行文件:bash

cmake -B cmake-build-debug
cmake --build cmake-build-debug
複製代碼

在項目根目錄運行生成的可執行文件:ide

./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
複製代碼

添加自定義命令和生成的文件

示例程序地址

假設,出於本教程的目的,咱們決定再也不使用平臺日誌和exp函數,而是但願生成一個可在 mysqrt 函數中使用的預計算值表。在本節中,咱們將在構建過程當中建立表,而後將該表編譯到咱們的應用程序中。

首先,讓咱們取消對 MathFunctions/CMakeLists.txt 中的 logexp 函數的檢查。而後從 mysqrt.cxx 中刪除對 HAVE_LOGHAVE_EXP 的檢查。同時,咱們能夠刪除 #include <cmath>

MathFunctions 子目錄中,提供了一個名爲 MakeTable.cxx 的新源文件來生成表。

// A simple program that builds a sqrt table
#include <cmath>
#include <fstream>
#include <iostream>

int main(int argc, char *argv[]) {
    // make sure we have enough arguments
    if (argc < 2) {
        return 1;
    }

    std::ofstream fout(argv[1], std::ios_base::out);
    const bool fileOpen = fout.is_open();
    if (fileOpen) {
        fout << "double sqrtTable[] = {" << std::endl;
        for (int i = 0; i < 10; ++i) {
            fout << sqrt(static_cast<double>(i)) << "," << std::endl;
        }
        // close the table with a zero
        fout << "0};" << std::endl;
        fout.close();
    }
    return fileOpen ? 0 : 1; // return 0 if wrote the file
}
複製代碼

咱們能夠看到生成的表不是簡單的文本,而是一段C++代碼。而且該文件的文件名是由參數傳入決定的。

下一步是將適當的命令添加到 MathFunctions/CMakeLists.txt 文件中,以構建MakeTable 可執行文件,而後在構建過程當中運行它。須要一些命令來完成此操做。

首先,在 MathFunctions/CMakeLists.txt 的頂部,添加 MakeTable 的可執行文件,就像添加任何其餘可執行文件同樣。

# first we add the executable that generates the table
# 首先,咱們添加生成表的可執行文件
add_executable(MakeTable MakeTable.cxx)
複製代碼

而後,咱們添加一個自定義命令,該命令指定如何經過運行 MakeTable 來產生 Table.h

# add the command to generate the source code
# 添加命令以生成源代碼
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 the main library
# 添加主庫
add_library(MathFunctions
        mysqrt.cxx
        ${CMAKE_CURRENT_BINARY_DIR}/Table.h
        )
複製代碼

咱們還必須將當前的二進制目錄添加到包含目錄列表中,以便 mysqrt.cxx 能夠找到幷包含 Table.h

# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
# 說明與咱們連接的任何人都須要包含當前源目錄才能找到 MathFunctions.h,而咱們不須要。
# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
# Table.h include is an implementation detail
# state that we depend on our binary dir to find Table.h
# 聲明咱們依賴Tutorial_BINARY_DIR但消費者不依賴,由於包含Table.h是一個實現細節,咱們依賴二進制目錄來查找Table.h
target_include_directories(MathFunctions
        INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
        PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
        )
複製代碼

如今,使用生成的表。首先,修改 mysqrt.cxx 以包含 Table.h 。接下來,咱們能夠重寫 mysqrt 函數以使用該表:

double mysqrt(double x) {
    if (x <= 0) {
        return 0;
    }

    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 -B cmake-build-debug
cmake --build cmake-build-debug
複製代碼

在項目根目錄運行生成的可執行文件:

./cmake-build-debug/Tutorial 2
複製代碼

終端輸出:

Use the table to help find an initial value 
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
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
複製代碼

在項目根目錄運行生成的可執行文件:

./cmake-build-debug/Tutorial 12
複製代碼

終端輸出:

Computing sqrt of 12 to be 6.5
Computing sqrt of 12 to be 4.17308
Computing sqrt of 12 to be 3.52433
Computing sqrt of 12 to be 3.46462
Computing sqrt of 12 to be 3.4641
Computing sqrt of 12 to be 3.4641
Computing sqrt of 12 to be 3.4641
Computing sqrt of 12 to be 3.4641
Computing sqrt of 12 to be 3.4641
Computing sqrt of 12 to be 3.4641
The square root of 12 is 3.4641
複製代碼

生成安裝程序

示例程序地址

接下來,假設咱們想將項目分發給其餘人,以便他們可使用它。咱們但願在各類平臺上提供二進制和源代碼分發。這與咱們以前在 「安裝」 示例進行的安裝有些不一樣,在以前安裝中,咱們根據源代碼構建的二進制文件進行安裝。

在此示例中,咱們將構建支持二進制安裝和程序包管理功能的安裝程序包。爲此,咱們將使用 CPack 建立平臺特定的安裝程序。具體來講,咱們須要在頂級 CMakeLists.txt 文件的底部添加幾行。

# setup installer
# 設置安裝程序
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 內容以下:

This is a License file.
複製代碼

最後,咱們包含 CPack 模塊,該模塊將使用這些變量和當前系統的其餘一些屬性來設置安裝程序。

在項目根目錄運行命令編譯項目:

cmake -B cmake-build-debug
複製代碼

在項目根目錄運行命令構建二進制發行版

cd cmake-build-debug
cpack
複製代碼

在項目根目錄下生成了文件:

.
├── ...
├── Tutorial-1.0-Darwin.sh
├── Tutorial-1.0-Darwin.tar.gz
└── ...
複製代碼

注意:要指定生成器,請使用 -G 選項。對於多配置構建,請使用 -C 指定配置。例如:

cpack -G ZIP -C Debug
複製代碼

在項目根目錄運行命令構建源代碼分發

cd cmake-build-debug
cpack --config CPackSourceConfig.cmake
複製代碼

在項目根目錄下生成了文件:

.
├── ...
├── Tutorial-1.0-Source.tar.Z
├── Tutorial-1.0-Source.tar.bz2
├── Tutorial-1.0-Source.tar.gz
├── Tutorial-1.0-Source.tar.xz
└── ...
複製代碼

添加對儀表板的支持

示例程序地址

咱們已經在 "測試" 示例中爲咱們的項目定義了許多測試。如今,咱們只須要運行這些測試並將其提交到儀表板便可。爲了包括對儀表板的支持,咱們在頂層 CMakeLists.txt 中包含了 CTest 模塊。

將如下內容:

# 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 -B cmake-build-debug
複製代碼

在項目根目錄運行命令生成儀表板:

cd cmake-build-debug
ctest –D Experimental
# 或者:ctest -VV –D Experimental
複製代碼

注意:對於多配置生成器(例如Visual Studio),必須指定配置類型:

ctest [-VV] -C Debug –D Experimental
複製代碼

或者從 IDE中 構建 Experimental 目標。

ctest 將構建和測試項目,並將結果提交給Kitware公共儀表板。儀表板的結果將被上傳到Kitware的公共儀表板:my.cdash.org/index.php?p…,以下圖所示:

CMake使用教程系列文章

相關文章
相關標籤/搜索