CMake 教程 01
介绍
这篇CMake教程提供了手把手的指导,涵盖了CMake中大部分构建问题。查看示例项目中的不同主题一起工作非常有用的。这个教程文档和示例可以在CMake提供的源代码的Help/guide/tutorial目录中找到。每个步骤都是包含着代码的子目录构成,可以作为学习的启动步骤。这个教程的例子是循序渐进的,每一步都为前一步提供解决方案。
第1步,基础起始点
最基础的工程是从源代码构建的可执行文件。对于简单工程,CMakeLists只需要三行代码即可,这会作为我们教程的起始点。在Step1目录中创建如下的CMakeLists文件:
cmake_minimum_required(VERSION 3.10)# set the project nameproject(Tutorial)# add the executableadd_executable(Tutorial tutorial.cxx)
可以注意到上面的例子在CMakeLists.txt中使用了小写的命令字。CMake支持大写,小写和混合的命令。tutorial.cxx在Step1目录里提供,可以用来计算某个数的平方根。
添加版本号和配置的头文件
我们添加的第一个特性是给我们的运行程序和工程提供一个版本号。当然我们也可以在源代码中定义,使用CMakeLists.txt可以提供更多的便利。
首先,修改CMakeLists.txt文件,使用project()命令来设置项目名称和版本号。
cmake_minimum_required(VERSION 3.10)# set the project and versionproject(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@的值会被替代。
接下来修改tutorial.cxx文件,包含配置的头文件,TutorialConfig.h。
最后,让我们更新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++标准
接下来,让我们添加一些C++11的标准,在tutorial.cxx中使用std::stod来替代atof。同时,移除掉#include。
const double inputValue = std::stod(argv[1]);
我们需要在CMake代码中明确指定应该使用正确的编译标志。最简单的方法是在CMake中使用CMAKE_CXX_STANDARD变量来指定支持的C++标准。对于教程而言,将CMakeLists.txt中的CMAKE_CXX_STANDARD设置为11,同时将CMAKE_CXX_STANDARD_REQUIRED设置为True。确保CMAKE_CXX_STANDARD声明在add_executable之上。
cmake_minimum_required(VERSION 3.10)# set the project name and versionproject(Tutorial VERSION 1.0)# specify the C++ standardset(CMAKE_CXX_STANDARD 11)set(CMAKE_CXX_STANDARD_REQUIRED True)
编译并测试
运行cmake命令或者使用cmake-gui来配置项目,然后使用你选择的编译工具编译。
例如,在命令行中,可以导航到CMake源代码的Help/guide/tutorial目录,创建一个编译目录:
mkdir Step1_build
接下来,进入到编译目录中,运行CMake命令来配置项目同时生成一个原生的编译系统:
cd Step1_build
cmake ../Step1
然后调用编译命令来实际编译和链接项目:
cmake --build .
最后,使用新编译的Tutorial运行程序运行如下命令:
Tutorial 4294967296
Tutorial 10
Tutorial
第2步,添加库
现在,我们会向工程中添加库。这个库会包含我们对于计算某个数的平方根的实现代码。运行程序可以使用库提供的方法来代替编译器提供的方法。
对于教程而言,我们会将库代码放在MathFunctions的子目录中。这个目录已经包含了一个头文件MathFunctions.h,和一个源代码文件mysqrt.cxx。源文件有一个mysqrt的函数提供计算平方根功能。
在MathFunctions目录下的CMakeLists.txt文件中添加如下行:
add_library(MathFunctions mysqrt.cxx)
为了使用新添加的库,我们需要在顶层的CMakeLists.txt文件中添加add_subdirectory()调用,这样新库才会被构建。我们将新库添加到运行文件,同时将MathFunctions添加到包含目录中,这样mysqrt.h文件就可以被找到。顶层的CMakeLists.txt文件最后几行应该是下面这样:
# add the MathFunctions libraryadd_subdirectory(MathFunctions)# add the executableadd_executable(Tutorial tutorial.cxx)target_link_libraries(Tutorial PUBLIC MathFunctions)# add the binary tree toe the search path for include files# so that we will find TutorialConfig.htarget_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
现在我们可以将MathFunctions库作为可选项。尽管对于我们的教程而言,这样做毫无必要,但是在大项目中,这是常见的情况。第一步是在顶层的CMakeLists.txt中添加option选项。
option(USE_MYMATH "Use tutorial provided math implementation" ON)# configure a header file to pass some of the CMake settings# to the source codeconfigure_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 executableadd_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.htarget_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
注意我们使用了变量EXTRA_LIBS来收集需要连接到运行程序的可选库。变量EXTRA_INCLUDES用来收集可选的头文件。这是处理很多可选控件的典型做法,我们会在后面介绍更现代的处理方法。
对于源代码的处理更为直接。首先,在tutorial.cxx文件中,只在需要包含MathFunction.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_MATH,我们可以将其添加到TutorialConfig.h.in中:
#cmakedefine USE_MATH练习:为什么设置TutorialConfig.h.in要在定义USE_MYMATH选项之后