CMake使用教程(一)

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

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

開發環境:c++

  • macOS 10.14.6
  • CMake 3.15.1
  • CLion 2018.2.4

基礎項目

示例程序地址git

最基礎的項目是單個源代碼文件構建的可執行文件。github

本示例提供的源代碼文件是 tutorial.cxx,可用於計算數字的平方根。代碼以下:shell

// A simple program that computes the square root of a number
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
    }

    // convert input to double
    const double inputValue = atof(argv[1]);

    // calculate square root
    const double outputValue = sqrt(inputValue);
    std::cout << "The square root of " << inputValue << " is " << outputValue
              << std::endl;
    return 0;
}
複製代碼

對於簡單的項目,只需三行內容的 CMakeLists.txt 文件,這將是本教程的起點。在項目根目錄下建立一個 CMakeLists.txt 文件,其內容以下:緩存

# 設置運行此配置文件所需的CMake最低版本
cmake_minimum_required(VERSION 3.15)

# set the project name
# 設置項目名稱
project(Tutorial)

# add the executable
# 添加一個可執行文件
add_executable(Tutorial tutorial.cxx)
複製代碼

請注意,此示例在 CMakeLists.txt 文件中使用小寫命令。CMake 支持大寫,小寫和大小寫混合命令。bash

當前項目結構以下:編輯器

.
├── CMakeLists.txt
└── tutorial.cxx
複製代碼

在項目根目錄運行命令生成編譯中間文件以及 makefile 文件:ide

cmake .
複製代碼

命令執行後會在項目根目錄下生成文件,項目結構以下:

.
├── CMakeCache.txt
├── CMakeFiles
├── CMakeLists.txt
├── Makefile
├── cmake_install.cmake
└── tutorial.cxx
複製代碼

這樣源文件和生成的文件都混在一塊兒,不方便管理,建議使用一個專門的目錄管理這些生成的文件。這裏使用 CLion 默認生成文件目錄 cmake-build-debug,在項目根目錄運行編譯命令並指定生成文件目錄:

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

項目結構以下:

.
├── CMakeLists.txt
├── cmake-build-debug
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── Makefile
│   └── cmake_install.cmake
└── tutorial.cxx
複製代碼

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

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

命令執行後生成了可執行文件 Tutorial,項目結構以下:

.
 ├── CMakeLists.txt
 ├── cmake-build-debug
 │   ├── CMakeCache.txt
 │   ├── CMakeFiles
 │   ├── Makefile
+│   ├── Tutorial
 │   └── cmake_install.cmake
 └── tutorial.cxx
複製代碼

在項目根目錄運行生成的可執行文件且不攜帶參數:

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

終端輸出:

Usage: ./cmake-build-debug/Tutorial number
複製代碼

在項目根目錄運行生成的可執行文件並攜帶參數:

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

終端輸出:

The square root of 2 is 1.41421
複製代碼

添加版本號和配置頭文件

示例程序地址

咱們添加的第一個功能是爲咱們的可執行文件和項目提供版本號。雖然咱們能夠僅在源代碼中執行此操做,可是使用 CMakeLists.txt 能夠提供更大的靈活性。

首先,修改 CMakeLists.txt 文件以設置版本號。

project(Tutorial VERSION 1.0)
複製代碼

而後,配置頭文件以將版本號傳遞給源代碼:

# configure a header file to pass some of the CMake settings
# to the source code
# 配置頭文件以將某些CMake設置傳遞給源代碼
configure_file(TutorialConfig.h.in TutorialConfig.h)
複製代碼

因爲已配置的文件將被寫入二進制目錄,所以咱們必須將該目錄添加到路徑列表中以搜索包含文件。將如下行添加到CMakeLists.txt文件的末尾:

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
# 將二進制目錄添加到包含文件的搜索路徑中,以便咱們找到TutorialConfig.h
target_include_directories(Tutorial PUBLIC
        "${PROJECT_BINARY_DIR}"
        )
複製代碼

使用您喜歡的編輯器,在源目錄中使用如下內容建立 TutorialConfig.h.in

// the configured options and settings for Tutorial
// 教程的配置選項和設置
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
複製代碼

CMake 配置此頭文件時,會在二進制目錄下生成一個文件 TutorialConfig.h,會把 TutorialConfig.h.in 中的內容拷貝到裏面,只是把 @Tutorial_VERSION_MAJOR@@Tutorial_VERSION_MINOR@ 替換成在 CMakeLists.txt 的配置的 1 和 0。

這裏的 1 和 0 是怎麼和 Tutorial_VERSION_MAJORTutorial_VERSION_MINOR關聯上的? 在 project() 中指定了 VERSION 後,CMake 會把版本信息存儲在如下變量中:

  • PROJECT_VERSION, <PROJECT-NAME>_VERSION
  • PROJECT_VERSION_MAJOR, <PROJECT-NAME>_VERSION_MAJOR
  • PROJECT_VERSION_MINOR, <PROJECT-NAME>_VERSION_MINOR
  • PROJECT_VERSION_PATCH, <PROJECT-NAME>_VERSION_PATCH
  • PROJECT_VERSION_TWEAK, <PROJECT-NAME>_VERSION_TWEAK.

MAJORMINORPATCHTWEAK 分別表明着版本號的四位,好比版本號 1.2.3.4MAJOR=1MINOR=2PATCH=3TWEAK=4。版本號不必定非得是4位,能夠只有1位,只是最大爲4位。

這裏 PROJECT-NAME 值爲 Tutorial,因此能從 Tutorial_VERSION_MAJORTutorial_VERSION_MINOR 中讀取到版本信息。

當從頂層 CMakeLists.txt 調用 project() 命令時,該版本也存儲在變量 CMAKE_PROJECT_VERSION 中。

接下來,修改 tutorial.cxx 以包含配置的頭文 件TutorialConfig.h 和打印出版本號,以下所示:

// A simple program that computes the square root of a number
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>

+ #include "TutorialConfig.h"

int main(int argc, char *argv[]) {
    if (argc < 2) {
+ // report version
+ std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+ << Tutorial_VERSION_MINOR << std::endl;
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
    }

    // convert input to double
    const double inputValue = atof(argv[1]);

    // calculate square root
    const double outputValue = sqrt(inputValue);
    std::cout << "The square root of " << inputValue << " is " << outputValue
              << std::endl;
    return 0;
}
複製代碼

在項目根目錄運行命令編譯項目和生成可執行文件:

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

在項目根目錄運行生成的可執行文件且不攜帶參數:

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

終端輸出:

./cmake-build-debug/Tutorial Version 1.0
Usage: ./cmake-build-debug/Tutorial number
複製代碼

指定C++標準

示例程序地址

CMake 中啓用對特定 C ++ 標準的支持的最簡單方法是使用 CMAKE_CXX_STANDARD 變量。對於本教程,請將 CMakeLists.txt 文件中的 CMAKE_CXX_STANDARD 變量設置爲11,並將 CMAKE_CXX_STANDARD_REQUIRED 設置爲 True

# specify the C++ standard
# 指定C ++標準
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
複製代碼

接下來,經過在 tutorial.cxx 中用 std :: stod 替換 atof,將一些 C ++ 11 功能添加到咱們的項目中。同時,刪除 #include <cstdlib>

// A simple program that computes the square root of a number
#include <cmath>
- #include <cstdlib>
#include <iostream>
#include <string>

#include "TutorialConfig.h"

int main(int argc, char *argv[]) {
    if (argc < 2) {
        // report version
        std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
                  << Tutorial_VERSION_MINOR << std::endl;
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
    }

    // convert input to double
- const double inputValue = atof(argv[1]);
+ const double inputValue = std::stod(argv[1]);

    // calculate square root
    const double outputValue = sqrt(inputValue);
    std::cout << "The square root of " << inputValue << " is " << outputValue
              << std::endl;
    return 0;
}
複製代碼

在項目根目錄運行命令編譯項目和生成可執行文件:

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

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

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

終端輸出:

The square root of 2 is 1.41421
複製代碼

添加庫

示例程序地址

如今,咱們將添加一個庫到咱們的項目中,該庫用於計算數字的平方根,可執行文件可使用此庫,而不是使用編譯器提供的標準平方根函數。該庫有兩個文件:

  • MathFunctions.h

    double mysqrt(double x);
    複製代碼
  • mysqrt.cxx

    源文件有一個 mysqrt 的函數,該函數提供與編譯器的 sqrt 函數相似的功能。

    #include <iostream>
    
    #include "MathFunctions.h"
    
    // a hack square root calculation using simple operations
    double mysqrt(double x) {
        if (x <= 0) {
            return 0;
        }
    
        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;
        }
        return result;
    }
    複製代碼

在項目根目錄下建立一個文件夾 MathFunctions ,把該庫放在其下,在其下建立一個 CMakeLists.txt 文件,內容以下:

add_library(MathFunctions mysqrt.cxx)
複製代碼

爲了使用新庫,咱們將在頂層 CMakeLists.txt 文件中添加 add_subdirectory 調用,以便構建該庫。咱們將新庫添加到可執行文件,並將 MathFunctions 添加爲包含目錄,以即可以找到 mqsqrt.h 頭文件。頂級 CMakeLists.txt 文件的最後幾行如今應以下所示:

# add the MathFunctions library
# 添加 MathFunctions 庫
add_subdirectory(MathFunctions)

# add the executable
# 添加一個可執行文件
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
# 將二進制目錄添加到包含文件的搜索路徑中,以便咱們找到TutorialConfig.h
target_include_directories(Tutorial PUBLIC
        "${PROJECT_BINARY_DIR}"
        "${PROJECT_SOURCE_DIR}/MathFunctions"
        )
複製代碼

修改 tutorial.cxx 使用引入的庫,其內容以下:

// A simple program that computes the square root of a number
- #include <cmath>
#include <iostream>
#include <string>

#include "TutorialConfig.h"
+ #include "MathFunctions.h"

int main(int argc, char *argv[]) {
    if (argc < 2) {
        // report version
        std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
                  << Tutorial_VERSION_MINOR << std::endl;
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
    }

    // convert input to double
    const double inputValue = std::stod(argv[1]);

    // calculate square root
- const double outputValue = sqrt(inputValue);
+ const double outputValue = mysqrt(inputValue);
    std::cout << "The square root of " << inputValue << " is " << outputValue
              << std::endl;
    return 0;
}
複製代碼

在項目根目錄運行命令編譯項目和生成可執行文件:

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

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

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

提供選項

示例程序地址

如今讓咱們將 MathFunctions 庫設爲可選。雖然對於本教程而言確實不須要這樣作,可是對於大型項目來講,這是很常見的。第一步是向頂級 CMakeLists.txt 文件添加一個選項:

# should we use our own math functions
# 咱們應該使用本身的數學函數嗎
option(USE_MYMATH "Use tutorial provided math implementation" ON)
複製代碼

此選項將顯示在 CMake GUIccmake 中,默認值ON可由用戶更改。此設置將存儲在緩存中,所以用戶無需在每次在構建目錄上運行CMake時都設置該值。

下一個是使創建和連接 MathFunctions 庫成爲條件。爲此,咱們將頂級 CMakeLists.txt 文件的結尾更改成以下所示:

# add the MathFunctions library
# 添加 MathFunctions 庫
if (USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions)
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif ()

# add the executable
# 添加一個可執行文件
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
# 將二進制目錄添加到包含文件的搜索路徑中,以便咱們找到TutorialConfig.h
target_include_directories(Tutorial PUBLIC
        "${PROJECT_BINARY_DIR}"
        ${EXTRA_INCLUDES}
        )
複製代碼

請注意,這裏使用變量 EXTRA_LIBS 來收集全部可選庫,以供之後連接到可執行文件中。變量 EXTRA_INCLUDES 相似地用於可選的頭文件。當處理許多可選組件時,這是一種經典方法,咱們將在下一步中介紹現代方法。

對源代碼的相應更改很是簡單。首先,根據須要在 tutorial.cxx 中決定包含 MathFunctions 頭仍是 包含 <cmath>

// should we include the MathFunctions header?
#ifdef USE_MYMATH
#include "MathFunctions.h"
#else
#include <cmath>
#endif
複製代碼

而後,在同一文件中,使用 USE_MYMATH 來肯定使用哪一個平方根函數:

#ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
#else
  const double outputValue = sqrt(inputValue);
#endif
複製代碼

因爲源代碼如今須要 USE_MYMATH,所以可使用如下行將其添加到 TutorialConfig.h.in 中:

#cmakedefine USE_MYMATH
複製代碼

download 上根據本身的平臺下載對應版本的 cmake-gui,安裝後打開軟件,選擇源代碼目錄和生成文件,以下圖所示:

點擊左下角 Generate 按鈕,軟件會彈出的選擇項目生成器的彈窗,這裏默認就好,點擊點擊 Done 按鈕,cmake-gui 開始編譯項目,生成中間文件,而且能夠在軟件看到咱們爲用戶提供的選項:

這個時候 cmake-build-debug/TutorialConfig.h 的內容以下:

// the configured options and settings for Tutorial
// 教程的配置選項和設置
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0
#define USE_MYMATH
複製代碼

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

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

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

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

取消 cmake-gui 中的 USE_MYMATH 的勾選,點擊 Generate 按鈕從新編譯項目,這個時候 cmake-build-debug/TutorialConfig.h 的內容以下:

// the configured options and settings for Tutorial
// 教程的配置選項和設置
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0
/* #undef USE_MYMATH */
複製代碼

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

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

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

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

終端輸出:

The square root of 2 is 1.41421
複製代碼

CMake使用教程系列文章

相關文章
相關標籤/搜索