CMake 导出库的头文件GenerateExportHeader
generate_export_header简介
generate_export_header()用于为library export宏定义。生成一个适用于预处理的文件,包含用在library中的EXPORT宏定义。
适用场景:假设我们用了一个自定义library,为避免跟用到的其他库重名,但又不得不开放public接口,就可以应用到generate_export_header 导出library export宏定义,将非public接口隐藏,将public接口设置为可见(<base_name>_EXPORT)。
CMake v3.12新增对C项目支持,老版本只支持C++项目。
语法格式
GENERATE_EXPORT_HEADER( LIBRARY_TARGET
[BASE_NAME <base_name>]
[EXPORT_MACRO_NAME <export_macro_name>]
[EXPORT_FILE_NAME <export_file_name>]
[DEPRECATED_MACRO_NAME <deprecated_macro_name>]
[NO_EXPORT_MACRO_NAME <no_export_macro_name>]
[STATIC_DEFINE <static_define>]
[NO_DEPRECATED_MACRO_NAME <no_deprecated_macro_name>]
[DEFINE_NO_DEPRECATED]
[PREFIX_NAME <prefix_name>])
目标属性 CXX_VISIBILITY_PRESET 和 VISIBILITY_INLINES_HIDDEN 能用于为目标添加合适的编译flags。
场景
默认情况下,generate_export_header() 在一个由library名称决定的文件中生成宏定义名称。意味着在最简单情况下,GenerateExportHeader的用户相当于:
# CMake片段
set(CMAKE_CXX_VISIBILITY_PRESET hidden) # 与generate_export_header搭配使用, 设置函数可见性
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) # 与generate_export_header搭配使用, 设置内联函数可见性
add_library(somelib someclass.cpp) # 生成library somelib
generate_export_header(somelib) # 为somelib export宏定义, 生成的文件名为somelib_export.h, 路径: ${CMAKE_CURRENT_BINARY_DIR}
install(TARGEST somelib DESTINATION ${LIBRARY_INSTALL_DIR}) # 安装目标somelib(库文件)到目标目录 (拷贝)
install(FILES
someclass.h
${PROJECT_BINARY_DIR}/somelib_export.h DESTINATION ${INCLUDE_INSTALL_DIR}
) # 安装文件someclass.h, somelib_export.h 到指定目标目录 (拷贝)
在ABI头文件:
#include "somelib_export.h"
class SOMELIB_EXPORT SomeClass { # public 类, 提供给用户使用, 使用SOMELIB_EXPORT声明, 避免重名
...
};
上面的CMake 片段将会在 ${CMAKE_CURRENT_BINARY_DIR}路径下生成名为somelib_export.h的文件,包含宏定义 SOMELIB_EXPORT, SOMELIB_NO_EXPORT, SOMELIB_DEPRECATED, SOMELIB_DEPRECATED_EXPORT 以及 SOMELIB_DEPRECATED_NO_EXPORT。生成的结果文件,应该和库中其他头文件一起安装。
BASE_NAME
BASE_NAME参数能用于重写(override)文件名和宏定义名称:
add_library(somelib someclass.cpp)
generate_export_header(somelib
BASE_NAME other_name
)
生成一个名为other_name_export.h的文件,包含宏定义OTHER_NAME_EXPORT, OTHER_NAME_NO_EXPORT 和 OTHER_NAME_DEPRECATED等等。
BASE_NAME也可能通过在函数中指定其他选项而被重写。例如:
add_library(somelib someclass.cpp)
generate_export_header(somelib
EXPORT_MACRO_NAME OTHER_NAME_EXPORT
)
OTHER_NAME_EXPORT
创建宏定义OTHER_NAME_EXPORT替换SOME_EXPORT,但其他宏定义和生成的文件名为默认值:
add_library(somelib someclass.cpp)
generate_export_header(somelib
DEPRECATED_MACRO_NAME KDE_DEPRECATED
)
创建宏定义KDE_DEPRECATED替换SOMELIB_DEPRECATED。
如果LIBRARY_TARGET是一个静态库,宏定义将被定义,但没有值。
同源构建shared, static library
如果同样的源被用于创建一个shared和static library,大写的${BASE_NAME}_STATIC_DEFIN 应该被用于构建该static library:
add_library(shared_variant SHARED ${lib_SRCS}) # 创建shared库
add_library(static_variant ${lib_SRCS}) # 用相同源文件创建static库
generate_export_header(shared_variant BASE_NAME libshared_and_static) # 会导出名为libshared_and_static_export.h的头文件
set_target_properties(static_variant PROPERTIES
COMPILE_FLAGS -DLIBSHARED_AND_STATIC_STATIC_DEFINE)
上述代码会导致export宏定义在构建static library时,扩展为空。
DEFINE_NO_DEPRECATED
如果指定了DEFINE_NO_DEPRECATED,那么一个宏${BASE_NAME}_NO_DEPRECATED 将会被定义。该宏能用于从预处理输出中移除deprecated code(不推荐使用的代码):
option(EXCLUDE_DEPRECATED "Exclude depreacted parts of the library" FLASE)
if (EXCLUDE_DEPRECATED)
set(NO_BUILD_DEPRECATED DEFINE_NO_DEPRECATED)
endif()
generate_export_header(somelib ${NO_BUILD_DEPRECATED})
接着,在somelib中:
class SOMELIB_EXPORT SomeClass
{
public:
#ifndef SOMELIB_NO_DEPRECATED
SOMELIB_DEPRECATED void oldMethod();
#endif
};
#ifndef SOMELIB_NO_DEPRECATED
void SomeClass::oldMethod() { }
#endif
PREFIX_NAME
如果指定了PREFIX_NAME,参数将用于所有生成的宏定义前缀:
generate_export_header(somelib PREFIX_NAME VTK_)
这样,会生成宏定义VTK_SOMELIB_EXPORT等
ADD_COMPILER_EXPORT_FLAGS
ADD_COMPILER_EXPORT_FLAGS( [<output_variable>] )
如果支持的话,ADD_COMPILER_EXPORT_FLAGS函数添加 -fvisibility=hidden 到CMAKE_CXX_FLAGS,并且这将是一个Windows上的no-op(空操作),如果不需要用于exporting支持的额外的编译选项。你可能随意传递一个单一参数给ADD_COMPILER_EXPORT_FLAGS,将会用CXX_FLAGS填充,要求使能可见性支持使用中的编译器/体系。
该函数(ADD_COMPILER_EXPORT_FLAGS)已经不推荐使用。参考 CXX_VISIBILITY_PRESET and VISIBILITY_INLINES_HIDDEN。
其他细节
CMAKE_CXX_VISIBILITY_PRESET 变量
target创建时,target属性CXX_VISIBILITY_PRESET的默认值。CXX_VISIBILITY_PRESET属性值决定编译flags的符号可见性(symbol visibility),与编译选项有关,如-fvisibility=。该属性影响所有类型目标的源的编译。
CXX_VISIBILITY_PRESET影响普通函数。
-fvisibility=hidden 在解决多个动态库同名函数时,很有效。
CMAKE_VISIBILITY_INLINES_HIDDEN 变量
target创建时,target属性VISIBILITY_INLINES_HIDDEN的默认值。VISIBILITY_INLINES_HIDDEN属性值决定是否添加编译flag,以隐藏inline函数的符号,与编译选项-fvisibility-inlines-hidden有关。该属性影响所有类型目标的源的编译。
VISIBILITY_INLINES_HIDDEN影响inline函数。
参考
https://cmake.org/cmake/help/v3.0/module/GenerateExportHeader.html