• target_compile_features
  • target_compile_options
  • COMPILE_LANG_AND_ID
  • BUILD_INTERFACE

生成器表达式(Generator expressions)允许在许多目标属性的上下文中使用,比如 LINK LIBRARIES、INCLUDE DIRECTORIES、COMPILE DEFINITIONS 等。当使用命令来填充这些属性时,它们也可以被使用,例如 target_link_libraries()、target_include_directories()、target_compile_definitions() 等。

生成器表达式可以用于启用条件链接、编译时使用的条件定义、条件包含目录等。这些条件可能基于构建配置、目标属性、平台信息或任何其他可查询的信息。

有不同类型的生成器表达式,包括逻辑表达式、信息表达式和输出表达式。

逻辑表达式用于创建条件输出。基本的表达式是 0 和 1 表达式。

生成器表达式的一个常见用法是有条件地添加编译器标志,比如那些用于语言级别或警告的标志。一个很好的模式是将该信息关联到一个 INTERFACE 目标,从而允许该信息传播。让我们先构造一个接口目标,并指定所需的 c++ 标准 11,而不是使用 CMAKE CXX 标准。

# 指定 C++ 标准
# set(CMAKE_CXX_STANDARD 11)
# set(CMAKE_CXX_STANDARD_REQUIRED True)
# 将上面的代码替换为
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

接下来,我们为项目添加所需的编译器警告标志。由于警告标志因编译器的不同而不同,我们使用 COMPILE_LANG_AND_ID 生成器表达式来控制给定语言和一组编译器 ID 要应用哪些标志(注:将要使用的生成器表达式是在 3.15 中引入的,所以要更新 cmake_minimum_required 的版本为 3.15),如下所示:

set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)

我们看到警告标志被封装在一个 BUILD_INTERFACE 条件中。这样做是为了让我们已安装项目的使用者不会继承我们的警告标志。

修改 Mathfunctions/cmakelists.txt,以便所有编译目标都有一个 target_link_libraries() 调用我们设置的编译条件标识 tutorial_compiler_flags

# add the library that runs
add_library(MathFunctions MathFunctions.cxx)
# include(GenerateExportHeader)
# generate_export_header(MathFunctions)
# 将当前二进制目录添加到包含目录列表中
target_include_directories(MathFunctions 
            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# 通过 tutorial_compiler_flags 指定 MathFunctions 动态库的编译参数设置
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)

# 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)
  # 通过 tutorial_compiler_flags 指定 MakeTable 可执行程序的编译参数设置
  target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)

  # 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}
                        )
  
  # 通过 tutorial_compiler_flags 指定 SqrtLibrary 静态库的编译参数设置
  target_link_libraries(SqrtLibrary PRIVATE tutorial_compiler_flags)
  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)