CMake
是一種跨平臺的免費開源軟件工具,用於使用與編譯器無關的方法來管理軟件的構建過程。在 Android Studio
上進行 NDK
開發默認就是使用 CMake
管理 C/C++
代碼,所以在學習 NDK
以前最好對 CMake
有必定的瞭解。html
本文主要以翻譯 CMake
的官方教程文檔爲主,加上本身的一些理解,該教程涵蓋了 CMake
的常見使用場景。因爲能力有限,翻譯部分採用機翻+人工校對,翻譯有問題的地方,說聲抱歉。ios
開發環境:c++
示例程序地址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_MAJOR
、Tutorial_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
.MAJOR
、MINOR
、PATCH
、TWEAK
分別表明着版本號的四位,好比版本號 1.2.3.4
,MAJOR=1
、MINOR=2
、PATCH=3
、TWEAK=4
。版本號不必定非得是4位,能夠只有1位,只是最大爲4位。
這裏 PROJECT-NAME
值爲 Tutorial
,因此能從 Tutorial_VERSION_MAJOR
和 Tutorial_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
複製代碼
在 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 GUI
和 ccmake
中,默認值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
複製代碼