CMake教程-第 3 步:添加库的使用要求

该文档是基于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 3: Adding Usage Requirements for a Library

3.1 Exercise 1 - Adding Usage Requirements for a Library

Usage requirements of a target parameters allow for far better control over a library or executable’s link and include line while also giving more control over the transitive property of targets inside CMake. The primary commands that leverage usage requirements are:
目标参数的使用要求可以更好地控制库或可执行文件的链接和包含行,同时还能在 CMake 内部更好地控制目标的传递属性。利用使用要求的主要命令有:

  • target_compile_definitions()
  • target_compile_options()
  • target_include_directories()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

3.1.1 目标

Add usage requirements for a library.
添加库的使用要求。

3.1.2 Helpful Resources(有用的资源)

  • CMAKE_CURRENT_SOURCE_DIR

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

  • MathFunctions/CMakeLists.txt
  • CMakeLists.txt

3.1.4 Getting Started(入门指南)

In this exercise, we will refactor our code from Adding a Library to use the modern CMake approach. We will let our library define its own usage requirements so they are passed transitively to other targets as necessary. In this case, MathFunctions will specify any needed include directories itself. Then, the consuming target Tutorial simply needs to link to MathFunctions and not worry about any additional include directories.
在本练习中,我们将重构添加库中的代码,以使用现代 CMake 方法。我们将让我们的库定义自己的使用要求,以便在必要时将它们传递给其他目标。在这种情况下,MathFunctions 将自行指定所需的包含目录。然后,目标 Tutorial 只需链接到 MathFunctions,而不必担心任何额外的 include 目录。

The starting source code is provided in the Step3 directory. In this exercise, complete TODO 1 through TODO 3.
Step3 目录中提供了起始源代码。在本练习中,完成 TODO 1 到 TODO 3。

First, add a call to target_include_directories() in MathFunctions/CMakeLists. Remember that CMAKE_CURRENT_SOURCE_DIR is the path to the source directory currently being processed.
首先,在 MathFunctions/CMakeLists 中添加对 target_include_directories() 的调用。请记住,CMAKE_CURRENT_SOURCE_DIR 是当前正在处理的源代码目录的路径。

Then, update (and simplify!) the call to target_include_directories() in the top-level CMakeLists.txt.
然后,更新(并简化!)顶层 CMakeLists.txt 中对 target_include_directories() 的调用。

3.1.5 Build and Run(构建并运行)

Make a new directory called Step3_build, run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool or by using cmake --build . from the build directory. Here’s a refresher of what that looks like from the command line:
新建一个名为 Step3_build 的目录,运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用你选择的构建工具或从构建目录中使用 cmake --build .构建项目。下面是命令行的简要说明:

mkdir Step3_build
cd Step3_build
cmake ../Step3
cmake --build .

Next, use the newly built Tutorial and verify that it is working as expected.
接下来,使用新构建的教程,并验证其是否按预期运行。

3.1.6 解决方案

Let’s update the code from the previous step to use the modern CMake approach of usage requirements.
让我们更新上一步的代码,使用现代 CMake 方法来满足使用要求。

We want to state that anybody linking to MathFunctions needs to include the current source directory, while MathFunctions itself doesn’t. This can be expressed with an INTERFACE usage requirement. Remember INTERFACE means things that consumers require but the producer doesn’t.
我们希望规定,链接到 MathFunctions 的任何人都需要包含当前源代码目录,而 MathFunctions 本身则不需要。这可以用 INTERFACE 使用要求来表达。请记住,INTERFACE 意味着消费者需要但生产者不需要的东西。

At the end of MathFunctions/CMakeLists.txt, use target_include_directories() with the INTERFACE keyword, as follows:
MathFunctions/CMakeLists.txt 的末尾,使用带有 INTERFACE 关键字的 target_include_directories(),如下所示:

TODO 1: Click to show/hide answer

TODO 1: MathFunctions/CMakeLists.txt
target_include_directories(MathFunctions
                           INTERFACE ${
    
    CMAKE_CURRENT_SOURCE_DIR}
                           )

Now that we’ve specified usage requirements for MathFunctions we can safely remove our uses of the EXTRA_INCLUDES variable from the top-level CMakeLists.txt.
既然我们已经指定了 MathFunctions 的使用要求,就可以放心地从 CMakeLists.txt 顶层删除 EXTRA_INCLUDES 变量的使用了。

Remove this line:

TODO 2: Click to show/hide answer

TODO 2: CMakeLists.txt
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")

And the lines:

TODO 3: Click to show/hide answer

TODO 3: CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

The remaining code looks like:

Click to show/hide the resulting code

Remaining code after removing EXTRA_INCLUDES
# 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}"
                           )

Notice that with this technique, the only thing our executable target does to use our library is call target_link_libraries() with the name of the library target. In larger projects, the classic method of specifying library dependencies manually becomes very complicated very quickly.
请注意,使用这种技术,我们的可执行目标要使用我们的库,唯一要做的就是调用 target_link_libraries(),并输入目标库的名称。在大型项目中,手动指定库依赖关系的传统方法很快就会变得非常复杂。

3.1.7 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# TODO 4: Replace the following code by:
# * Creating an interface library called tutorial_compiler_flags
#   Hint: use add_library() with the INTERFACE signature
# * Add compiler feature cxx_std_11 to tutorial_compiler_flags
#   Hint: Use target_compile_features()

# 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: Remove EXTRA_INCLUDES list

# add the MathFunctions library
add_subdirectory(MathFunctions)
# list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")

# add the executable
add_executable(Tutorial tutorial.cxx)

# TODO 5: Link Tutorial to tutorial_compiler_flags
                                                                                                                                                                                                                   
target_link_libraries(Tutorial PUBLIC MathFunctions)

# TODO 3: Remove use of EXTRA_INCLUDES

# 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}
                           )

3.1.8 MathFunctions/CMakeLists.txt

add_library(MathFunctions MathFunctions.cxx)

# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
target_include_directories(MathFunctions
                           INTERFACE ${
    
    CMAKE_CURRENT_SOURCE_DIR}
                           )

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")

  # library that just does sqrt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

  # TODO 7: Link SqrtLibrary to tutorial_compiler_flags

  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
endif()                                                                                                                                                                                                            

# TODO 6: Link MathFunctions to tutorial_compiler_flags

3.1.9 运行结果

执行Tutorial程序的运行结果如下所示:

$ ./Tutorial 25
Computing sqrt of 25 to be 13
Computing sqrt of 25 to be 7.46154
Computing sqrt of 25 to be 5.40603
Computing sqrt of 25 to be 5.01525
Computing sqrt of 25 to be 5.00002
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
The square root of 25 is 5
$ 

3.2 Exercise 2 - Setting the C++ Standard with Interface Libraries

Now that we have switched our code to a more modern approach, let’s demonstrate a modern technique to set properties to multiple targets.
既然我们已经将代码转换为更现代的方法,那么让我们来演示一下为多个目标设置属性的现代技术。

Let’s refactor our existing code to use an INTERFACE library. We will use that library in the next step to demonstrate a common use for generator expressions.
让我们重构现有代码,使用 INTERFACE 库。我们将在下一步中使用该库来演示生成器表达式的常用用法。

3.2.1 目标

Add an INTERFACE library target to specify the required C++ standard.
添加 INTERFACE 库目标,指定所需的 C++ 标准。

3.2.2 Helpful Resources(有用的资源)

  • add_library()
  • target_compile_features()
  • target_link_libraries()

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

  • CMakeLists.txt
  • MathFunctions/CMakeLists.txt

3.2.4 Getting Started(入门指南)

In this exercise, we will refactor our code to use an INTERFACE library to specify the C++ standard.
在本练习中,我们将重构代码,使用 INTERFACE 库来指定 C++ 标准。

Start this exercise from what we left at the end of Step3 exercise 1. You will have to complete TODO 4 through TODO 7.
从步骤 3 练习 1 结束时留下的内容开始本练习。您必须完成 TODO 4 到 TODO 7。

Start by editing the top level CMakeLists.txt file. Construct an INTERFACE library target called tutorial_compiler_flags and specify cxx_std_11 as a target compiler feature.
首先编辑顶层 CMakeLists.txt 文件。创建名为 tutorial_compiler_flagsINTERFACE 库目标,并指定 cxx_std_11 为目标编译器特征。

Modify CMakeLists.txt and MathFunctions/CMakeLists.txt so that all targets have a target_link_libraries() call to tutorial_compiler_flags.
修改 CMakeLists.txtMathFunctions/CMakeLists.txt,使所有目标都有 target_link_libraries() 调用 tutorial_compiler_flags

3.2.5 Build and Run(构建并运行)

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

cd Step3_build
cmake --build .

Next, use the newly built Tutorial and verify that it is working as expected.
接下来,使用新构建的教程,并验证其是否按预期运行。

3.2.6 解决方案

Let’s update our code from the previous step to use interface libraries to set our C++ requirements.
让我们更新上一步的代码,使用接口库来设置 C++ 要求。

To start, we need to remove the two set() calls on the variables CMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED. The specific lines to remove are as follows:
首先,我们需要删除变量 CMAKE_CXX_STANDARDCMAKE_CXX_STANDARD_REQUIRED 上的两个 set() 调用。需要删除的具体行列如下:

CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Next, we need to create an interface library, tutorial_compiler_flags. And then use target_compile_features() to add the compiler feature cxx_std_11.
接下来,我们需要创建一个接口库 tutorial_compiler_flags。然后使用 target_compile_features() 添加编译器特性 cxx_std_11

TODO 4: Click to show/hide answer

TODO 4: CMakeLists.txt
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

Finally, with our interface library set up, we need to link our executable Target, our MathFunctions library, and our SqrtLibrary library to our new tutorial_compiler_flags library. Respectively, the code will look like this:
最后,设置好界面库后,我们需要将可执行目标、MathFunctions 库和 SqrtLibrary 库链接到新的 tutorial_compiler_flags 库。代码将分别如下:

TODO 5: Click to show/hide answer

TODO 5: CMakeLists.txt
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

this:

TODO 6: Click to show/hide answer

TODO 6: MathFunctions/CMakeLists.txt
  target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)

and this:

TODO 7: Click to show/hide answer

TODO 7: MathFunctions/CMakeLists.txt
  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)

With this, all of our code still requires C++ 11 to build. Notice though that with this method, it gives us the ability to be specific about which targets get specific requirements. In addition, we create a single source of truth in our interface library.
这样,我们的所有代码仍然需要 C++ 11 才能构建。不过请注意,有了这种方法,我们就能明确哪些目标需要特定的要求。此外,我们还在接口库中创建了一个单一的真相源。

3.2.7 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# TODO 4: Replace the following code by:
# * Creating an interface library called tutorial_compiler_flags
#   Hint: use add_library() with the INTERFACE signature
# * Add compiler feature cxx_std_11 to tutorial_compiler_flags
#   Hint: Use target_compile_features()

add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

# 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: Remove EXTRA_INCLUDES list

# add the MathFunctions library
add_subdirectory(MathFunctions)
# list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")

# add the executable
add_executable(Tutorial tutorial.cxx)

# TODO 5: Link Tutorial to tutorial_compiler_flags
# target_link_libraries(Tutorial PUBLIC MathFunctions)
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

# TODO 3: Remove use of EXTRA_INCLUDES                                                                                                                                                                             

# 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}
                           )

3.2.8 MathFunctions/CMakeLists.txt

add_library(MathFunctions MathFunctions.cxx)

# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
target_include_directories(MathFunctions
                           INTERFACE ${
    
    CMAKE_CURRENT_SOURCE_DIR}
                           )

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")

  # library that just does sqrt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

  # TODO 7: Link SqrtLibrary to tutorial_compiler_flags
  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)

endif()

# TODO 6: Link MathFunctions to tutorial_compiler_flags
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)

3.2.9 运行结果

执行Tutorial程序的运行结果如下所示:

$ ./Tutorial 25
Computing sqrt of 25 to be 13
Computing sqrt of 25 to be 7.46154
Computing sqrt of 25 to be 5.40603
Computing sqrt of 25 to be 5.01525
Computing sqrt of 25 to be 5.00002
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
The square root of 25 is 5
$ ./Tutorial 100
Computing sqrt of 100 to be 50.5
Computing sqrt of 100 to be 26.2401
Computing sqrt of 100 to be 15.0255
Computing sqrt of 100 to be 10.8404
Computing sqrt of 100 to be 10.0326
Computing sqrt of 100 to be 10.0001
Computing sqrt of 100 to be 10
Computing sqrt of 100 to be 10
Computing sqrt of 100 to be 10
Computing sqrt of 100 to be 10
The square root of 100 is 10
$

猜你喜欢

转载自blog.csdn.net/u014100559/article/details/133281152