CMake入门(一)

春节期间停更了一段时间,Mojolicious暂时先告一段落。最近翻译一下CMake官方文档——CMake Tutorial。

https://cmake.org/cmake/help/latest/guide/tutorial/index.html

同时,在这里推荐一个中文的教程:

https://github.com/SFUMECJF/cmake-examples-Chinese

一、简介

CMake教程提供了一个循序渐进的指南,涵盖了CMake使用中常见的录中找到。每个步骤都有自己的子目录,其中包含可以用作起点的代码。教程示例是渐进的,因此每个步骤都为前一个步骤提供了完整的解决方案。

二、一个基本的起点(step1)

最简单也是最基础的例子便是生成一个可执行文件。一个最简单的项目中,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@的值将被替换。

下一个修改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++标准

接下来,让我们通过用tutorial.cxx中的std:: stand替换atof,为我们的项目添加一些c++ 11特性。同时,删除#include <cstdlib>。

const double inputValue = std::stod(argv[1]);

我们需要在CMake代码中明确声明它应该使用正确的标志。在CMake中启用对特定c++标准的支持的最简单方法是使用CMAKE_CXX_STANDARD变量。对于本教程,将cmakellists .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 version
project(Tutorial VERSION 1.0)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

四、编译和测试

运行cmake可执行文件或cmake-gui来配置项目,然后用你选择的构建工具构建它。例如,我们可以从命令行导航到CMake源代码树的Help/guide/tutorial目录,并创建一个构建目录:

mkdir Step1_build

接下来,导航到build目录,运行CMake来配置项目并生成一个本地构建系统:

cd Step1_build
cmake ../Step1

然后调用构建系统来实际编译/链接项目:

cmake --build .

最后,尝试用以下命令来使用新构建的tutorial:

Tutorial 4294967296
Tutorial 10
Tutorial

五、增加库(step2)

现在我们将向我们的项目添加一个库。这个库将包含我们自己的计算数字平方根的实现。可执行文件可以使用这个库,而不是编译器提供的标准平方根函数。

在本教程中,我们将把这个库放入名为MathFunctions的子目录中。这个目录已经包含了一个头文件MathFunctions.h和一个源文件mysqrt.cxx。源文件有一个名为mysqrt的函数,它提供了与编译器的sqrt函数类似的功能。

将这个仅有一行的CMakeLists.txt文件添加到MathFunctions目录:

add_library(MathFunctions mysqrt.cxx)

为了使用这个新库,我们将在顶级的CMakeLists.txt文件中添加一个add_subdirectory()调用,以便构建这个库。我们将新的库添加到可执行文件中,并将MathFunctions作为包含目录,这样就可以找到mysqrt.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。在下面一行:

#cmakedefine USE_MYMATH

练习:为什么配置TutorialConfig.h很重要?在USE_MYMATH?如果我们把这两个颠倒过来会发生什么?

运行cmake可执行文件或cmake-gui来配置项目,然后用你选择的构建工具构建它。然后运行构建的教程可执行文件。

现在让我们更新USE_MYMATH的值。最简单的方法是使用cmake-gui或ccmake(如果你在终端上的话)。或者,如果你想从命令行更改选项,试试:

cmake ../Step2 -DUSE_MYMATH=OFF

重新生成并再次运行教程。

哪个函数给出了更好的结果,sqrt还是mysqrt?

标签