GenerateExportHeader

本模块提供用于生成包含预处理器宏定义以控制 C/C++ 符号可见性的头文件的命令。

在 CMake 项目中加载此模块,使用

include(GenerateExportHeader)

版本 3.12 新增:支持 C 项目。以前的版本仅支持 C++ 项目。

在开发 C 或 C++ 项目时,特别是针对跨平台使用时,符号可见性决定了哪些函数、类、全局变量、模板和其他符号对库的用户可见。

例如,在 Windows 上,构建共享库时必须明确标记符号为 __declspec(dllexport),使用时标记为 __declspec(dllimport)。其他平台可能使用诸如 __attribute__((visibility("default"))) 的属性。

此模块简化了预处理器宏的创建和使用,以管理这些要求,避免了源代码中重复且易错的 #ifdef 块。

某些符号可见性也可以通过编译器选项控制。在 CMake 中,目标属性(例如 <LANG>_VISIBILITY_PRESETVISIBILITY_INLINES_HIDDEN)在适当的情况下启用编译器可见性标志。另请参阅相关的便捷变量 CMAKE_<LANG>_VISIBILITY_PRESETCMAKE_VISIBILITY_INLINES_HIDDEN,以在当前作用域中为所有目标启用它。这些通常与本模块结合使用,以进一步简化 C/C++ 代码,消除源代码中对某些预处理器宏的需求。

命令

本模块提供以下命令

生成导出头文件

generate_export_header

生成一个适合包含在源代码中的头文件,其中包含用于控制符号可见性的预处理器导出

generate_export_header(
  <target>
  [BASE_NAME <base-name>]
  [EXPORT_FILE_NAME <export-file-name>]
  [EXPORT_MACRO_NAME <export-macro-name>]
  [NO_EXPORT_MACRO_NAME <no-export-macro-name>]
  [DEPRECATED_MACRO_NAME <deprecated-macro-name>]
  [DEFINE_NO_DEPRECATED]
  [NO_DEPRECATED_MACRO_NAME <no-deprecated-macro-name>]
  [STATIC_DEFINE <static-define>]
  [PREFIX_NAME <prefix>]
  [CUSTOM_CONTENT_FROM_VARIABLE <variable>]
  [INCLUDE_GUARD_NAME <include-guard-name>]
)

默认情况下,此命令在当前二进制目录 (CMAKE_CURRENT_BINARY_DIR) 中生成一个名为 <target-name-lowercase>_export.h 的头文件。此头文件定义了一组预处理器宏,用于在不同平台和构建类型(例如,静态或共享构建)中将 API 符号标记为导出、隐藏或弃用,并且旨在与库的公共头文件一起安装,因为它影响公共 API 声明

  • <MACRO>_EXPORT:标记要导出或导入的符号,使其在构建或使用共享库时作为公共 API 的一部分可见。

  • <MACRO>_NO_EXPORT:标记不应导出的符号。如果 <LANG>_VISIBILITY_PRESET 目标属性设置为 hidden,则在源代码中使用此宏通常是多余的。

  • <MACRO>_DEPRECATED:将符号标记为已弃用。当使用此类符号时,编译器会在编译时发出警告。

  • <MACRO>_DEPRECATED_EXPORT:将导出/导入和弃用标记组合用于既是公共 API 的一部分又已弃用的符号。

  • <MACRO>_DEPRECATED_NO_EXPORT:标记不应导出的已弃用符号(内部且已弃用)。

  • <MACRO>_NO_DEPRECATED:一个可在源代码中使用的宏,用于通过预处理器逻辑有条件地将已弃用代码部分从构建中排除。

默认情况下,<MACRO> 部分派生自目标的名称大写形式或明确提供的 <base-name>。所有宏名称都可以使用可选参数进行自定义。

参数为

<target>

将为其生成导出头文件的目标名称。支持的目标类型:

  • STATIC 库(在这种情况下,导出相关宏被定义为无值)

  • SHARED

  • MODULE

  • 版本 3.1 新增:OBJECT

BASE_NAME <base-name>

如果指定,它会覆盖默认的文件名和宏名称。

EXPORT_FILE_NAME <export-file-name>

如果指定,它将生成的导出头文件 (<base-name-lowercase>_export.h) 的完整路径和名称覆盖为 <export-file-name>。如果作为相对路径给定,它将相对于当前二进制目录 (CMAKE_CURRENT_BINARY_DIR) 进行解释。

EXPORT_MACRO_NAME <export-macro-name>

如果指定,它会覆盖导出指令的默认宏名称。

NO_EXPORT_MACRO_NAME <no-export-macro-name>

如果指定,<no-export-macro-name> 将用于指定不应导出项属性的宏名称。

DEPRECATED_MACRO_NAME <deprecated-macro-name>

如果指定,将使用以下名称:

  • <deprecated-macro-name>(用于标记已弃用符号的宏)

  • <deprecated-macro-name>_EXPORT(用于带导出标记的已弃用符号的宏)

  • <deprecated-macro-name>_NO_EXPORT(用于带无导出标记的已弃用符号的宏)

而不是默认的 <MACRO>_DEPRECATED{,_EXPORT,_NO_EXPORT} 格式的名称。

DEFINE_NO_DEPRECATED

如果指定,这将定义一个名为 <MACRO>_NO_DEPRECATED 的宏。

NO_DEPRECATED_MACRO_NAME <no-deprecated-macro-name>

DEFINE_NO_DEPRECATED 选项结合使用。如果指定,则使用名为 <no-deprecated-macro-name> 的宏,而不是默认的 <MACRO>_NO_DEPRECATED

STATIC_DEFINE <static-define>

如果指定,将使用 <static-define> 宏名称,而不是默认的 <MACRO>_STATIC_DEFINE。此宏控制生成头文件中静态库的符号导出行为。它通常用于从相同源文件构建库的共享和静态变体时,使用单个生成的导出头文件。当为静态库定义此宏时,导出相关宏将展开为空。这在 Windows 上也很重要,因为符号修饰仅对共享库需要,对静态库不需要。

PREFIX_NAME <prefix>

如果指定,附加的 <prefix> 将预置到所有生成的宏名称前。

CUSTOM_CONTENT_FROM_VARIABLE <variable>

3.7 版本中新增。

如果指定,来自 <variable> 值的內容将附加到生成的头文件内容,在预处理器宏定义之后。

INCLUDE_GUARD_NAME <include-guard-name>

3.11 版本新增。

如果指定,<include-guard-name> 将用作预处理器宏名称,以保护生成的头文件免于多次包含,而不是默认名称 <export-macro-name>_H

<base-name-lowercase>_export.h
#ifndef <include-guard-name>
#define <include-guard-name>
// ...
#endif /* <include-guard-name> */

已弃用的命令

add_compiler_export_flags

自版本 3.0 起已弃用:请改用目标属性 CXX_VISIBILITY_PRESETVISIBILITY_INLINES_HIDDEN

将 C++ 编译器选项 -fvisibility=hidden(如果支持,还有 -fvisibility-inlines-hidden)添加到 CMAKE_CXX_FLAGS 变量或指定变量,以默认隐藏所有符号

add_compiler_export_flags([<output_variable>])

此命令在 Windows 上是空操作,因为它不需要额外的编译器标志来支持导出。

<output-variable>

可选变量名,将填充一个由空格分隔的 C++ 编译选项字符串,这些选项是为正在使用的编译器/架构启用可见性支持所必需的。如果指定此参数,则 CMAKE_CXX_FLAGS 变量将不会被修改。

示例

示例:生成导出头文件

以下示例演示了如何使用此模块在当前二进制目录 (example_export.h) 中生成导出头文件,并将其用于名为 example 的 C++ 库中,以控制符号可见性。生成的头文件定义了预处理器宏 EXAMPLE_EXPORTEXAMPLE_NO_EXPORTEXAMPLE_DEPRECATEDEXAMPLE_DEPRECATED_EXPORTEXAMPLE_DEPRECATED_NO_EXPORT,并与库的其他公共头文件一起安装

CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(GenerateExportHeaderExample)

# Set default visibility of all symbols to hidden
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)

add_library(example)

include(GenerateExportHeader)
generate_export_header(example)

target_sources(
  example
  PRIVATE example.cxx
  PUBLIC
    FILE_SET HEADERS
      FILES example.h
    FILE_SET generated_headers
      TYPE HEADERS
      BASE_DIRS $<TARGET_PROPERTY:example,BINARY_DIR>
      FILES ${CMAKE_CURRENT_BINARY_DIR}/example_export.h
)

target_include_directories(example PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

install(
  TARGETS example
  FILE_SET HEADERS
  FILE_SET generated_headers
)

在 ABI 头文件中

example.h
#include "example_export.h"

// This class is part of the public API and is exported
class EXAMPLE_EXPORT SomeClass
{
public:
  SomeClass();
  void doSomething();

  // This method is deprecated
  EXAMPLE_DEPRECATED void legacyMethod();
};

// This function is exported and deprecated
EXAMPLE_DEPRECATED_EXPORT void legacyPublicFunction();

// This function is deprecated but not exported
EXAMPLE_DEPRECATED void legacyInternalFunction();
example.cxx
#include <iostream>
#include "example.h"

SomeClass::SomeClass() = default;

void SomeClass::doSomething()
{
  std::cout << "SomeClass::doSomething() called" << std::endl;
}

void SomeClass::legacyMethod()
{
  std::cout << "SomeClass::legacyMethod() is deprecated" << std::endl;
}

void legacyPublicFunction()
{
  std::cout << "legacyPublicFunction() is deprecated" << std::endl;
}

void internalLegacyFunction()
{
  std::cout << "legacyInternalFunction() is deprecated" << std::endl;
}

示例:自定义生成头文件

BASE_NAME 参数可用于覆盖生成的文件名和宏名称。以下将生成一个名为 other_name_export.h 的文件,其中包含导出相关的宏,例如 OTHER_NAME_EXPORTOTHER_NAME_NO_EXPORTOTHER_NAME_DEPRECATED 等。

add_library(example example.cxx)
include(GenerateExportHeader)
generate_export_header(example BASE_NAME "other_name")

BASE_NAME 可通过指定其他命令选项进行覆盖。例如,以下代码创建了一个宏 OTHER_NAME_EXPORT 而不是 EXAMPLE_EXPORT,但其他宏和生成的头文件名仍设置为其默认值

add_library(example example.cxx)
include(GenerateExportHeader)
generate_export_header(example EXPORT_MACRO_NAME "OTHER_NAME_EXPORT")

以下示例创建了 KDE_DEPRECATED 宏,而不是默认的 EXAMPLE_DEPRECATED

add_library(example example.cxx)
include(GenerateExportHeader)
generate_export_header(example DEPRECATED_MACRO_NAME "KDE_DEPRECATED")

DEFINE_NO_DEPRECATED 选项可用于定义一个宏,该宏可用于从预处理器输出中删除已弃用代码

option(EXCLUDE_DEPRECATED "Exclude deprecated parts of the library")
if(EXCLUDE_DEPRECATED)
  set(NO_BUILD_DEPRECATED DEFINE_NO_DEPRECATED)
endif()

include(GenerateExportHeader)
generate_export_header(example ${NO_BUILD_DEPRECATED})
example.h
class EXAMPLE_EXPORT SomeClass
{
public:
#ifndef EXAMPLE_NO_DEPRECATED
  EXAMPLE_DEPRECATED void legacyMethod();
#endif
};
example.cxx
#ifndef EXAMPLE_NO_DEPRECATED
void SomeClass::legacyMethod() {  }
#endif

PREFIX_NAME 参数可用于在所有生成的宏名称前添加前缀。例如,以下将生成诸如 VTK_SOMELIB_EXPORT 等宏。

include(GenerateExportHeader)
generate_export_header(somelib PREFIX_NAME "VTK_")

通过 CUSTOM_CONTENT_FROM_VARIABLE 参数可以将附加内容追加到生成的头文件。

include(GenerateExportHeader)
set(content [[#include "project_api.h"]])
generate_export_header(example CUSTOM_CONTENT_FROM_VARIABLE content)

示例:构建共享和静态库

在以下示例中,共享库和静态库都从相同的源文件构建,并且定义了 <MACRO>_STATIC_DEFINE 宏编译定义,以确保生成的导出头文件对两者都有效

add_library(example_shared SHARED example.cxx)
add_library(example_static STATIC example.cxx)

include(GenerateExportHeader)
generate_export_header(example_shared BASE_NAME "example")

# Define macro to disable export attributes for static build
target_compile_definitions(example_static PRIVATE EXAMPLE_STATIC_DEFINE)

示例:升级已弃用命令

在 CMake 的早期版本中,add_compiler_export_flags() 命令用于添加符号可见性编译选项

CMakeLists.txt
add_library(example example.cxx)

include(GenerateExportHeader)

add_compiler_export_flags(flags)
string(REPLACE " " ";" flags "${flags}")
set_property(TARGET example APPEND PROPERTY COMPILE_OPTIONS "${flags}")

generate_export_header(example)

在新代码中,以下目标属性用于实现相同的功能

CMakeLists.txt
add_library(example example.cxx)

include(GenerateExportHeader)

set_target_properties(
  example
  PROPERTIES
    CXX_VISIBILITY_PRESET hidden
    VISIBILITY_INLINES_HIDDEN TRUE
)

generate_export_header(example)

另请参阅

  • DEFINE_SYMBOL 目标属性用于自定义生成头文件所使用的预处理器宏名称。此宏决定了库头文件是在库自身编译期间还是在其他项目(例如,安装后)使用它时被包含。

  • ENABLE_EXPORTS 目标属性。

  • WINDOWS_EXPORT_ALL_SYMBOLS 目标属性。