CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile 或者 project 文件,能测试编译器所支持的 C++ 特性,类似 UNIX 下的 automake 。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。
首先从CMake下载页面下载最新的源码包,解压缩之后可以查看所有的源码,包括我们下面需要用到的CMake 教程。
CMake 教程提供了逐步指南,涵盖了 CMake 可以解决的常见构建系统问题。了解示例项目中各个主题如何协同工作将非常有帮助。教程文档和示例的源代码可以在 CMake 源代码树的Help/guide/tutorial
目录中找到 。每个步骤都有其自己的子目录,其中包含可以用作起点的代码。教程示例是渐进式的,因此每个步骤都为上一步提供了完整的解决方案,如下所示:
最基本的项目是从源代码文件构建的可执行文件。对于简单的项目,只需三行CMakeLists.txt
文件。这将是本教程的起点。CMakeLists.txt
在Step1
目录中创建一个 文件,如下所示:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(Tutorial)
# add the executable
add_executable(Tutorial tutorial.cxx)
请注意,此示例在CMakeLists.txt
文件中使用小写命令。CMake 支持大写,小写和大小写混合命令。Step1
目录中tutorial.cxx
提供的源代码,可用于计算数字的平方根。
我们将添加的第一个功能是为我们的可执行文件和项目提供版本号。虽然我们可以在源代码中专门执行此操作,但使用 CMakeLists.txt
可以提供更大的灵活性。
首先,修改CMakeLists.txt
文件以使用project()
命令设置项目名称和版本号。
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
然后,配置头文件以将版本号传递给源代码:
configure_file(TutorialConfig.h.in TutorialConfig.h)
由于已配置的文件将被写入二进制树,因此我们必须将该目录添加到路径列表中以搜索包含文件。将以下行添加到CMakeLists.txt
文件的末尾:
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 生成这个头文件时,@Tutorial_VERSION_MAJOR@
和 @Tutorial_VERSION_MINOR@
的值将会由 CMakeLists.txt
中对应的值替换。接下来我们将头文件包含到 tutorial.cxx
中并且使用这个版本号,代码如下:
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;
}
接下来, 向我们的项目中添加一些 C++ 11 功能。
const double inputValue = std::stod(argv[1]);
我们将需要在 CMake 代码中明确声明应使用正确的标志。在 CMake 中启用对特定 C++ 标准的支持的最简单方法是使用CMAKE_CXX_STANDARD
变量。对于本教程,请设置CMAKE_CXX_STANDARD
将CMakeLists.txt
文件中的变量设置为11并把CMAKE_CXX_STANDARD_REQUIRED
改为True:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用您选择的构建工具进行构建。
例如,从命令行我们可以导航到Help/guide/tutorialCMake
源代码树的目录并运行以下命令:
mkdir Step1_build
cd Step1_build
cmake ../Step1
cmake --build .
导航到构建 Tutorial 的目录(可能是make目录或Debug或Release构建配置子目录),然后运行以下命令:
Tutorial 4294967296
Tutorial 10
Tutorial
现在,我们将库添加到我们的项目中。该库将包含我们自己的实现,用于计算数字的平方根。然后可执行文件可以使用此库,而不是使用编译器提供的标准平方根函数。
在本教程中,我们将库放入名为的子目录中 MathFunctions 。该目录已经包含一个头文件 MathFunctions.h 和一个源文件mysqrt.cxx。源文件具有一个mysqrt
功能,该功能提供与编译器sqrt
功能相似的功能。
本教程中将这个库放到名为 MathFunctions 的子文件夹中,这个子文件夹需要包含一个 CMakeLists.txt 文件,文件中有如下一行:
add_library(MathFunctions mysqrt.cxx)
为了利用新库,我们将添加一个 add_subdirectory()
调用顶级 CMakeLists.txt 文件,以便构建库。我们将新库添加到可执行文件,并添加 MathFunctions 为包含目录,以便可以找到头文件 mqsqrt.h。现在,顶级 CMakeLists.txt 文件的最后几行应如下所示:
# add the MathFunctions library
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
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
现在让我们将 MathFunctions 库设为可选。虽然对于本教程而言确实没有任何必要,但是对于较大的项目,这是常见的情况。第一步是向顶层 CMakeLists.txt 文件添加一个选项 。
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
此选项将显示在 cmake-gui 和 ccmake 用户可以更改的默认值 ON。此设置将存储在缓存中,因此用户无需在每次在构建目录上运行 CMake 时都设置该值。
下一个更改是使建立和链接 MathFunctions 库成为条件。为此,我们将顶级 CMakeLists.txt 文件的末尾更改为如下所示:
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
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
请注意,使用变量EXTRA_LIBS
来收集所有可选库,以便以后链接到可执行文件中。该变量 EXTRA_INCLUDES
类似地用于可选的头文件。当处理许多可选组件时,这是一种经典方法,我们将在下一步中介绍现代方法。
对源代码的相应更改非常简单。首先,如果需要,请在 tutorial.cxx 中包含 MathFunctions.h 头文件:
#ifdef USE_MYMATH
# include "MathFunctions.h"
#endif
然后,在同一文件中,USE_MYMATH
控制使用哪个平方根函数:
#ifdef USE_MYMATH
const double outputValue = mysqrt(inputValue);
#else
const double outputValue = sqrt(inputValue);
#endif
在源代码中我们同样使用了 USE_MYMATH
变量。通过在 TutorialConfig.h.in 中添加如下配置,Cmake 将这个变量引入到源代码中:
#cmakedefine USE_MYMATH
使用要求可以更好地控制库或可执行文件的链接并包含行,同时还可以更好地控制 CMake 内部目标的传递属性。利用使用需求的主要命令是:
target_compile_definitions()
target_compile_options()
target_include_directories()
target_link_libraries()
让我们从添加库(Step 2)中重构代码,以使用现代 CMake 使用需求方法。我们首先声明,链接到 MathFunctions 的任何人都需要包括当前源目录,而 MathFunctions 本身不需要。因此这可能成为INTERFACE
使用要求。
记住INTERFACE
是指消费者需要的东西,而生产者则不需要。将以下行添加到 MathFunctions/CMakeLists.txt 的末尾 :
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
现在我们已经为math_functions
指定了使用要求,我们可以安全地从顶级 CMakeLists.txt 中删除EXTRA_INCLUDES
变量的使用:
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
以及这里:
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
完成后,运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用您选择的构建工具或通过cmake --build
构建目录进行构建。 .