• 将原来的 静态库 MathFunctions拆分为动态库 MathFunctions.dll 和静态库 SqrtLibrary.lib 两个库。MathFunctions.dll 动态库会使用 SqrtLibrary.lib 静态库。
  • BUILD_SHARED_LIBS

在顶层的 CMakeLists.txt 文件中添加 BUILD_SHARED_LIBS,如果这个标记存在且为真,则将导致所有库都被构建为共享库,除非该库被显式地设置为静态库。这个变量通常作为 option() 添加到项目中,以便项目的每个用户可以决定他们是要使用共享库还是静态库构建项目。

顶层的 CMakeLists.txt 文件修改如下:

# 设置CMake版本最低要求
cmake_minimum_required(VERSION 3.10)

# 设置项目名称和版本
project(Tutorial VERSION 3.2)

# 指定 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# control where the static and shared libraries are built so that on windows
# we don't need to tinker with the path to run the executable
# 指定静态库和动态库的生成路径
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")

# 生成共享库选项
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

# 生成一个头文件,传递 CMake 的一些设置到源代码
configure_file(TutorialConfig.h.in TutorialConfig.h)

# 添加编译子模块 MathFunctions library
add_subdirectory(MathFunctions)

# 添加源码文件和生成的目标文件的名称
add_executable(Tutorial main.cpp)

# 依赖共享库 MathFunctions
target_link_libraries(Tutorial PUBLIC MathFunctions)

# 添加头文件查找路径
# target_include_directories(Tutorial PUBLIC 
#                             "${PROJECT_BINARY_DIR}"
#                             "${PROJECT_SOURCE_DIR}/MathFunctions"
#                         )
target_include_directories(Tutorial PUBLIC 
                            "${PROJECT_BINARY_DIR}"
)

## 安装
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  DESTINATION include
)

# enable dashboard scripting
include(CTest)

# does the application run
add_test(NAME Runs COMMAND Tutorial 25)

# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# define a function to simplify adding tests
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction()

# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")

## Packaging an Installer
# 此模块将打包项目当前平台所需的任何运行时库
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)

Mathfunctions/Cmakelists.txt 中,我们需要创建一个 Sqrtlibrary,当启用 USE_MYMATH 时,将有条件地构建和安装。

# add the library that runs
add_library(MathFunctions MathFunctions.cxx)
# 将当前二进制目录添加到包含目录列表中
target_include_directories(MathFunctions 
            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)

# should we use our own math functions
# 是否使用 USE_MYMATH 选项
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
  # 设置程序中使用到的宏定义
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")

  # first we add the executable that generates the table
  add_executable(MakeTable MakeTable.cxx)

  # add the command to generate the source code
  add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    DEPENDS MakeTable
    )

  # 添加静态库
  # library that just does sqrt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
              )

  # state that we depend on our binary dir to find Table.h
  target_include_directories(SqrtLibrary 
                             PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
                             )
    
  # state that SqrtLibrary need PIC(position independent code) when the default is shared libraries
  # 如果将一个没有位置独立代码(position independent code)的静态库与一个有位置独立代码的库组合在一起使用,
  # 就要显示设置目标的属性,否则会有链接错误
  set_target_properties(SqrtLibrary PROPERTIES
                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                        )

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

# define the symbol stating we are using the declspec(dllexport) when
# building on windows
# 声明dll导出宏定义
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")

# install rules
# 安装规则
set(installable_libs MathFunctions)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()

install(TARGETS ${installable_libs} DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)