CMake
是一种跨平台的免费开源软件工具,用于使用与编译器无关的方法来管理软件的构建过程。在 Android Studio
上进行 NDK
开发默认就是使用 CMake
管理 C/C++
代码,所以在学习 NDK
以前最好对 CMake
有必定的了解。php
本文主要以翻译 CMake
的官方教程文档为主,加上本身的一些理解,该教程涵盖了 CMake
的常见使用场景。因为能力有限,翻译部分采用机翻+人工校对,翻译有问题的地方,说声抱歉。html
开发环境:ios
示例程序地址c++
在上一步 “系统自检” 中,除了在 TutorialConfig.h
中保存 HAVE_LOG
和 HAVE_EXP
值以外,还有更好的作法吗?对于此示例,咱们将尝试使用 target_compile_definitions
。git
首先,从 TutorialConfig.h.in
中删除上一步的定义,在 mysqrt.cxx
中再也不包含 TutorialConfig.h
,移除上一步在 MathFunctions/CMakeLists.txt
中增长的额外包含。github
接下来,咱们能够将 HAVE_LOG
和 HAVE_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
中的 log
和 exp
函数的检查。而后从 mysqrt.cxx
中删除对 HAVE_LOG
和 HAVE_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…,以下图所示: