CMake教程-第 2 步 添加一个库

该文档是基于CMake的官方教程翻译而来,并稍微添加了自己的理解:

cmake的官方网站为:CMake Tutorial

1 CMake教程介绍

The CMake tutorial provides a step-by-step guide that covers common build system issues that CMake helps address. Seeing how various topics all work together in an example project can be very helpful.
CMake 教程提供了一个循序渐进的指南,涵盖了 CMake 可帮助解决的常见构建系统问题。在一个示例项目中了解各个主题是如何协同工作的,会非常有帮助。

2 学习步骤

The tutorial source code examples are available in this archive. Each step has its own subdirectory containing code that may be used as a starting point. The tutorial examples are progressive so that each step provides the complete solution for the previous step.
本文档中提供了教程源代码示例。每个步骤都有自己的子目录,其中包含可用作起点的代码。教程示例是循序渐进的,因此每一步都提供了前一步的完整解决方案。

Step 1: A Basic Starting Point

  • Exercise 1 - Building a Basic Project
  • Exercise 2 - Specifying the C++ Standard
  • Exercise 3 - Adding a Version Number and Configured Header File

Step 2: Adding a Library

  • Exercise 1 - Creating a Library
  • Exercise 2 - Adding an Option

Step 3: Adding Usage Requirements for a Library

  • Exercise 1 - Adding Usage Requirements for a Library
  • Exercise 2 - Setting the C++ Standard with Interface Libraries

Step 4: Adding Generator Expressions

  • Exercise 1 - Adding Compiler Warning Flags with Generator Expressions

Step 5: Installing and Testing

  • Exercise 1 - Install Rules
  • Exercise 2 - Testing Support

Step 6: Adding Support for a Testing Dashboard

  • Exercise 1 - Send Results to a Testing Dashboard

Step 7: Adding System Introspection

  • Exercise 1 - Assessing Dependency Availability

Step 8: Adding a Custom Command and Generated File

Step 9: Packaging an Installer

Step 10: Selecting Static or Shared Libraries

Step 11: Adding Export Configuration

Step 12: Packaging Debug and Release

3 Step 2: Adding a Library

At this point, we have seen how to create a basic project using CMake. In this step, we will learn how to create and use a library in our project. We will also see how to make the use of our library optional.
至此,我们已经了解了如何使用 CMake 创建一个基本项目。在这一步中,我们将学习如何在项目中创建和使用库。我们还将了解如何选择使用库。

3.1 Exercise 1 - Creating a Library(练习 1 - 创建一个库)

To add a library in CMake, use the add_library() command and specify which source files should make up the library.
要在 CMake 中添加一个库,请使用 add_library() 命令并指定组成该库的源文件。

Rather than placing all of the source files in one directory, we can organize our project with one or more subdirectories. In this case, we will create a subdirectory specifically for our library. Here, we can add a new CMakeLists.txt file and one or more source files. In the top level CMakeLists.txt file, we will use the add_subdirectory() command to add the subdirectory to the build.
我们可以用一个或多个子目录来组织项目,而不是将所有源文件放在一个目录中。在本例中,我们将专门为库创建一个子目录。在这里,我们可以添加一个新的 CMakeLists.txt 文件和一个或多个源文件。在顶层 CMakeLists.txt 文件中,我们将使用 add_subdirectory() 命令将子目录添加到构建中。

Once the library is created, it is connected to our executable target with target_include_directories() and target_link_libraries().
一旦创建了库,就可以使用 target_include_directories()target_link_libraries() 将其连接到我们的可执行目标。

3.1.1 目标

Add and use a library.
添加并使用库。

3.1.2 Helpful Resources(有用的资源)

  • add_library()
  • add_subdirectory()
  • target_include_directories()
  • target_link_libraries()
  • PROJECT_SOURCE_DIR

3.1.3 Files to Edit(需编辑的文件)

  • CMakeLists.txt
  • tutorial.cxx
  • MathFunctions/CMakeLists.txt

3.1.4 Getting Started(入门指南)

In this exercise, we will add a library to our project that contains our own implementation for computing the square root of a number. The executable can then use this library instead of the standard square root function provided by the compiler.
在本练习中,我们将在项目中添加一个库,其中包含我们自己实现的数字平方根计算方法。然后,可执行文件就可以使用这个库,而不是编译器提供的标准平方根函数。

For this tutorial we will put the library into a subdirectory called MathFunctions. This directory already contains the header files MathFunctions.h and mysqrt.h. Their respective source files MathFunctions.cxx and mysqrt.cxx are also provided. We will not need to modify any of these files. mysqrt.cxx has one function called mysqrt that provides similar functionality to the compiler’s sqrt function. MathFunctions.cxx contains one function sqrt which serves to hide the implementation details of sqrt.
在本教程中,我们将把函数库放到名为 MathFunctions 的子目录中。这个目录已经包含了头文件 MathFunctions.h 和 mysqrt.h。我们还提供了它们各自的源文件 MathFunctions.cxxmysqrt.cxx,我们不需要修改任何这些文件。mysqrt.cxx 有一个名为 mysqrt 的函数,提供与编译器的 sqrt 函数类似的功能。MathFunctions.cxx 包含一个函数 sqrt,用于隐藏 sqrt 的实现细节。

From the Help/guide/tutorial/Step2 directory, start with TODO 1 and complete through TODO 6.
从 Help/guide/tutorial/Step2 目录中的 TODO 1 开始,完成 TODO 6。

First, fill in the one line CMakeLists.txt in the MathFunctions subdirectory.
首先,在 MathFunctions 子目录下填写一行 CMakeLists.txt。

Next, edit the top level CMakeLists.txt.
接下来,编辑顶层的 CMakeLists.txt 文件。

Finally, use the newly created MathFunctions library in tutorial.cxx
最后,在 tutorial.cxx 中使用新创建的 MathFunctions 库

3.1.5 Build and Run(构建并运行)

Run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool.
运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用所选的构建工具构建项目。

Below is a refresher of what that looks like from the command line:
下面是命令行的简要说明:

mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .

Try to use the newly built Tutorial and ensure that it is still producing accurate square root values.
尝试使用新建的 Tutorial,确保它仍能生成准确的平方根值。

3.1.6 解决方案

In the CMakeLists.txt file in the MathFunctions directory, we create a library target called MathFunctions with add_library(). The source files for the library are passed as an argument to add_library(). This looks like the following line:
在 MathFunctions 目录下的 CMakeLists.txt 文件中,我们使用 add_library() 创建了一个名为 MathFunctions 的目标库。库的源文件作为参数传递给 add_library()。如下行所示

  • TODO 1: MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

To make use of the new library we will add an add_subdirectory() call in the top-level CMakeLists.txt file so that the library will get built.

  • TODO 2: Click to show/hide answer
TODO 2: CMakeLists.txt
add_subdirectory(MathFunctions)

Next, the new library target is linked to the executable target using target_link_libraries().
接下来,使用 target_link_libraries() 将新目标库链接到可执行目标。

  • TODO 3: Click to show/hide answer
TODO 3: CMakeLists.txt
target_link_libraries(Tutorial PUBLIC MathFunctions)

Finally we need to specify the library’s header file location. Modify target_include_directories() to add the MathFunctions subdirectory as an include directory so that the MathFunctions.h header file can be found.
最后,我们需要指定库的头文件位置。修改 target_include_directories(),将 MathFunctions 子目录添加为包含目录,以便找到 MathFunctions.h 头文件。

  • TODO 4: Click to show/hide answer
TODO 4: CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

Now let’s use our library. In tutorial.cxx, include MathFunctions.h:
现在,让我们使用我们的函数库。在 tutorial.cxx 中,包含 MathFunctions.h:

  • TODO 5: Click to show/hide answer
TODO 5: tutorial.cxx
#include "MathFunctions.h"

Lastly, replace sqrt with our library function mathfunctions::mysqrt.
最后,将 sqrt 替换为我们的库函数 mathfunctions::mysqrt。

  • TODO 6: Click to show/hide answer
TODO 6: tutorial.cxx
  const double outputValue = mathfunctions::sqrt(inputValue);

3.1.7 CMakeLists.txt

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)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# TODO 2: Use add_subdirectory() to add MathFunctions to this project
add_subdirectory(MathFunctions)

# add the executable
add_executable(Tutorial tutorial.cxx)

# TODO 3: Use target_link_libraries to link the library to our executable
target_link_libraries(Tutorial PUBLIC MathFunctions)

# TODO 4: Add MathFunctions to Tutorial's target_include_directories()                                                                                                                                             
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/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}"
                           )

3.1.8 MathFunctions/CMakeLists.txt

# TODO 14: Remove mysqrt.cxx from the list of sources

# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
# and mysqrt.cxx
# Hint: You will need the add_library command
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

# TODO 7: Create a variable USE_MYMATH using option and set default to ON

# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass
# USE_MYMATH as a precompiled definition to our source files

# TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with
# source mysqrt.cxx

# TODO 13: When USE_MYMATH is ON, link SqrtLibrary to the MathFunctions Library

3.1.9 tutorial.cxx

// A simple program that computes the square root of a number
#include <cmath>
#include <iostream>
#include <string>

// TODO 5: Include MathFunctions.h
#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]);

  // TODO 6: Replace sqrt with mathfunctions::sqrt
  const double outputValue = mathfunctions::sqrt(inputValue);

  // calculate square root
  //const double outputValue = sqrt(inputValue);
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;
  return 0;                                                                                                                                                                                                        
}

3.2 Exercise 2 - Adding an Option(练习2 - 添加一个选项)

Now let us add an option in the MathFunctions library to allow developers to select either the custom square root implementation or the built in standard implementation. While for the tutorial there really isn’t any need to do so, for larger projects this is a common occurrence.
现在,让我们在 MathFunctions 库中添加一个选项,允许开发人员选择自定义平方根实现或内置标准实现。虽然在教程中确实没有必要这样做,但对于大型项目来说,这是经常发生的事情。

CMake can do this using the option() command. This gives users a variable which they can change when configuring their cmake build. This setting will be stored in the cache so that the user does not need to set the value each time they run CMake on a build directory.
CMake 可以使用 option() 命令实现这一功能。这样,用户就可以在配置 cmake 编译时更改一个变量。该设置将保存在缓存中,因此用户无需每次在构建目录上运行 CMake 时都设置该值。

3.2.1 目标

Add the option to build without MathFunctions.
增加不使用 MathFunctions 的构建选项。

3.2.2 Helpful Resources(有用的资源)

  • if()
  • option()
  • target_compile_definitions()

3.2.3 Files to Edit(需要编辑的文件)

  • MathFunctions/CMakeLists.txt
  • MathFunctions/MathFunctions.cxx

3.2.4 Getting Started(入门指南)

Start with the resulting files from Exercise 1. Complete TODO 7 through TODO 14.
从练习 1 的结果文件开始。完成 TODO 7 到 TODO 14。

First create a variable USE_MYMATH using the option() command in MathFunctions/CMakeLists.txt. In that same file, use that option to pass a compile definition to the MathFunctions library.
首先,使用 MathFunctions/CMakeLists.txt 中的 option() 命令创建一个变量 USE_MYMATH。在同一文件中,使用该选项将编译定义传递给 MathFunctions 库。

Then, update MathFunctions.cxx to redirect compilation based on USE_MYMATH.
然后,更新 MathFunctions.cxx,根据 USE_MYMATH 重定向编译。

Lastly, prevent mysqrt.cxx from being compiled when USE_MYMATH is on by making it its own library inside of the USE_MYMATH block of MathFunctions/CMakeLists.txt.
最后,通过在 MathFunctions/CMakeLists.txtUSE_MYMATH 代码段中将 mysqrt.cxx 设为自己的库,防止在 USE_MYMATH 启用时编译 mysqrt.cxx

3.2.5 Build and Run(构建并运行)

Since we have our build directory already configured from Exercise 1, we can rebuild by simply calling the following:
由于我们已经在练习 1 中配置好了构建目录,因此只需调用以下命令即可重建:

cd ../Step2_build
cmake --build .

Next, run the Tutorial executable on a few numbers to verify that it’s still correct.
接下来,用几个数字去运行 Tutorial 可执行文件,以验证它是否仍然正确。

Now let’s update the value of USE_MYMATH to OFF. The easiest way is to use the cmake-gui or ccmake if you’re in the terminal. Or, alternatively, if you want to change the option from the command-line, try:
现在,让我们将 USE_MYMATH 的值更新为 OFF。最简单的方法是在终端使用 cmake-gui 或 ccmake。或者,如果你想通过命令行更改选项,可以尝试

cmake ../Step2 -DUSE_MYMATH=OFF

Now, rebuild the code with the following:
现在,使用以下代码重建代码:

cmake --build .

Then, run the executable again to ensure that it still works with USE_MYMATH set to OFF. Which function gives better results, sqrt or mysqrt?
然后,再次运行可执行文件,确保在将 USE_MYMATH 设置为 OFF 时仍能运行。sqrt 和 mysqrt 哪个函数的结果更好?

3.2.6 解决方案

The first step is to add an option to MathFunctions/CMakeLists.txt. This option will be displayed in the cmake-gui and ccmake with a default value of ON that can be changed by the user.
第一步是在 MathFunctions/CMakeLists.txt 中添加一个选项。该选项将显示在 cmake-gui 和 ccmake 中,默认值为 ON,用户可以更改。

  • TODO 7: Click to show/hide answer
TODO 7: MathFunctions/CMakeLists.txt
option(USE_MYMATH "Use tutorial provided math implementation" ON)

Next, make building and linking our library with mysqrt function conditional using this new option.
接下来,使用这一新选项使我们的程序库与 mysqrt 函数的构建和链接成为有条件的。

Create an if() statement which checks the value of USE_MYMATH. Inside the if() block, put the target_compile_definitions() command with the compile definition USE_MYMATH.
创建一条 if() 语句,检查 USE_MYMATH 的值。在 if() 块中,将 target_compile_definitions() 命令与编译定义 USE_MYMATH 放在一起。

  • TODO 8: Click to show/hide answer
TODO 8: MathFunctions/CMakeLists.txt
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()

When USE_MYMATH is ON, the compile definition USE_MYMATH will be set. We can then use this compile definition to enable or disable sections of our source code.
USE_MYMATH 开启时,编译定义 USE_MYMATH 将被设置。然后,我们就可以使用这个编译定义来启用或禁用源代码的各个部分。

The corresponding changes to the source code are fairly straightforward. In MathFunctions.cxx, we make USE_MYMATH control which square root function is used:
对源代码的相应修改相当简单。在 MathFunctions.cxx 中,我们让 USE_MYMATH 控制使用哪个平方根函数:

  • TODO 9: Click to show/hide answer
TODO 9: MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
  return detail::mysqrt(x);
#else
  return std::sqrt(x);
#endif

Next, we need to include mysqrt.h if USE_MYMATH is defined.
接下来,如果定义了 USE_MYMATH,我们需要包含 mysqrt.h。

  • TODO 10: Click to show/hide answer
TODO 10: MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
#  include "mysqrt.h"
#endif

Finally, we need to include cmath now that we are using std::sqrt.
最后,既然我们使用了 std::sqrt,就需要加入 cmath。

  • TODO 11: Click to show/hide answer
TODO 11 : MathFunctions/MathFunctions.cxx
#include <cmath>

At this point, if USE_MYMATH is OFF, mysqrt.cxx would not be used but it will still be compiled because the MathFunctions target has mysqrt.cxx listed under sources.
此时,如果关闭 USE_MYMATH,就不会使用 mysqrt.cxx,但它仍会被编译,因为 MathFunctions 目标程序的源代码中列出了 mysqrt.cxx。

There are a few ways to fix this. The first option is to use target_sources() to add mysqrt.cxx from within the USE_MYMATH block. Another option is to create an additional library within the USE_MYMATH block which is responsible for compiling mysqrt.cxx. For the sake of this tutorial, we are going to create an additional library.
有几种方法可以解决这个问题。第一种方法是使用 target_sources(),在 USE_MYMATH 代码块中添加 mysqrt.cxx。另一种方法是在 USE_MYMATH 代码块中创建一个额外的库,负责编译 mysqrt.cxx。在本教程中,我们将创建一个附加库。

First, from within USE_MYMATH create a library called SqrtLibrary that has sources mysqrt.cxx.
首先,在 USE_MYMATH 中创建一个名为 SqrtLibrary 的库,其源代码为 mysqrt.cxx。

  • TODO 12: Click to show/hide answer
TODO 12 : MathFunctions/CMakeLists.txt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

Next, we link SqrtLibrary onto MathFunctions when USE_MYMATH is enabled.
接下来,当启用 USE_MYMATH 时,我们将 SqrtLibrary 链接到 MathFunctions

  • TODO 13: Click to show/hide answer
TODO 13 : MathFunctions/CMakeLists.txt
  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)

Finally, we can remove mysqrt.cxx from our MathFunctions library source list because it will be pulled in when SqrtLibrary is included.
最后,我们可以从 MathFunctions 库源代码列表中删除 mysqrt.cxx,因为当 SqrtLibrary 被包含时,它将被调入。

  • TODO 14: Click to show/hide answer
TODO 14 : MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx)

With these changes, the mysqrt function is now completely optional to whoever is building and using the MathFunctions library. Users can toggle USE_MYMATH to manipulate what library is used in the build.
有了这些更改,mysqrt 函数现在对于构建和使用 MathFunctions 函数库的用户来说完全是可选的了。用户可以切换 USE_MYMATH 来控制在构建过程中使用哪个库。

3.2.7 CMakeLists.txt

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)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# TODO 2: Use add_subdirectory() to add MathFunctions to this project
add_subdirectory(MathFunctions)

# add the executable
add_executable(Tutorial tutorial.cxx)

# TODO 3: Use target_link_libraries to link the library to our executable
target_link_libraries(Tutorial PUBLIC MathFunctions)

# TODO 4: Add MathFunctions to Tutorial's target_include_directories()                                                                                                                                             
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/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}"
                           )

3.2.8 MathFunctions/CMakeLists.txt

# TODO 14: Remove mysqrt.cxx from the list of sources
add_library(MathFunctions MathFunctions.cxx)

# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
# and mysqrt.cxx
# Hint: You will need the add_library command
# add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

# TODO 7: Create a variable USE_MYMATH using option and set default to ON

# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass
# USE_MYMATH as a precompiled definition to our source files
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()

# TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with
# source mysqrt.cxx
add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

# TODO 13: When USE_MYMATH is ON, link SqrtLibrary to the MathFunctions Library
target_link_libraries(MathFunctions PUBLIC SqrtLibrary)

3.2.9 MathFunctions/MathFunctions.cxx

#include "MathFunctions.h"

// TODO 11: include cmath
#include <cmath>

// TODO 10: Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH
#ifdef USE_MYMATH
#include "mysqrt.h"
#endif

namespace mathfunctions {
double sqrt(double x)
{
  // TODO 9: If USE_MYMATH is defined, use detail::mysqrt.
#ifdef USE_MYMATH
  return detail::mysqrt(x);
#else
  return std::sqrt(x);
#endif
  // Otherwise, use std::sqrt.
  // return detail::mysqrt(x);
}
}
09-22 01:11