GenerateExportHeader¶
本模块提供用于生成包含预处理器宏定义以控制 C/C++ 符号可见性的头文件的命令。
在 CMake 项目中加载此模块,使用
include(GenerateExportHeader)
版本 3.12 新增:支持 C 项目。以前的版本仅支持 C++ 项目。
在开发 C 或 C++ 项目时,特别是针对跨平台使用时,符号可见性决定了哪些函数、类、全局变量、模板和其他符号对库的用户可见。
例如,在 Windows 上,构建共享库时必须明确标记符号为 __declspec(dllexport)
,使用时标记为 __declspec(dllimport)
。其他平台可能使用诸如 __attribute__((visibility("default")))
的属性。
此模块简化了预处理器宏的创建和使用,以管理这些要求,避免了源代码中重复且易错的 #ifdef
块。
某些符号可见性也可以通过编译器选项控制。在 CMake 中,目标属性(例如 <LANG>_VISIBILITY_PRESET
和 VISIBILITY_INLINES_HIDDEN
)在适当的情况下启用编译器可见性标志。另请参阅相关的便捷变量 CMAKE_<LANG>_VISIBILITY_PRESET
和 CMAKE_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_PRESET
和VISIBILITY_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_EXPORT
、EXAMPLE_NO_EXPORT
、EXAMPLE_DEPRECATED
、EXAMPLE_DEPRECATED_EXPORT
和 EXAMPLE_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_EXPORT
、OTHER_NAME_NO_EXPORT
、OTHER_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)
示例:升级已弃用命令¶
在 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
目标属性。