原来一直自己写库的导出信息,但看qt的库引用方式非常好,今天仔细研究了下,弄明白了一部分,记录下。
先说明下它最大的好处。cmake提供的这个功能,可以
自动适应不同
的编译条件,像debug/release/…,不同的编译输出会存到target不同的变量里
使用cmake功能生成配置文件
其实,cmake早已做好了这部分工作,只是之前没注意到这个功能。
输出库的配置(ModuleTargets.cmake)
用法很简单:
install(TARGETS myexe EXPORT myproj DESTINATION bin) # 设置这个库的输出名为`myproj`
install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj) # 输出名为`myproj`的配置文件
其中,myexe
是当前的target,EXPORT
表示这个target要被输出,输出的配置名叫myproj
。那么配置文件的名默认为myproj.cmake
使用这个功能的好处
让cmake为不同的编译条件生成不同的targets<Config>.cmake文件,例如在debug模式时生成
ModuleTarget-Debug.cmake
。
cmake会同时生成ModuleTargets.cmake
文件,最终只需include这个文件即可
输出库的查找文件(Module-config.cmake)
这个文件是find_package
时要用的,详见cmake : 详解find_package。
在Module-config.cmake
里include(ModuleTargets.cmake)
,然后就可放心的使用find_package()
。
cmake也为这个config文件设置了帮助函数
include(CMakePackageConfigHelpers)
configure_package_config_file(
<input> # 输入的config.cmake.in,需要预先准备
<output> # 输出的文件
INSTALL_DESTINATION <path> # 输出的路径,可以是绝对路径,如果不是绝对路径,会基于最后一个参数INSTALL_PREFIX计算绝对路径
[PATH_VARS <var1> <var2> ... <varN>] # 生成的变量,命名方式为`PACKAKE_VAR`,这些变量必须在config.cmake.in里使用
[NO_SET_AND_CHECK_MACRO] # 不生成 set_and_check 宏,这个宏用于检查路径是否合法
[NO_CHECK_REQUIRED_COMPONENTS_MACRO] # 不生成check_required_components宏,这个宏用于检查依赖项是否存在
[INSTALL_PREFIX <path>] # 安装路径,必须是绝对路径,不设置时默认使用 CMAKE_INSTALL_PREFIX
)
使用示例(cmake官方文档)
CmakeLists.txt
set(INCLUDE_INSTALL_DIR include/ ... CACHE )
set(LIB_INSTALL_DIR lib/ ... CACHE )
set(SYSCONFIG_INSTALL_DIR etc/foo/ ... CACHE )
#...
include(CMakePackageConfigHelpers)
configure_package_config_file(FooConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
INSTALL_DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake
PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
VERSION 1.2.3
COMPATIBILITY SameMajorVersion )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake )
预先配置好的config.cmake.in
set(FOO_VERSION x.y.z)
...
@PACKAGE_INIT@ #!!! 这行后,参数中的宏才会生效
...
set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(FOO_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
check_required_components(Foo)
输出版本的配置(module-config-version.cmake)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_BINARY_DIR}/cmake/eccad-version-config.cmake
VERSION 0.1.8
COMPATIBILITY AnyNewerVersion
)
这里只有一点要非常注意,版本配置的文件名要和库配置文件名配对。具体点就是,
<TargetName>-config.cmake
配置<TargetName>-config-version.cmake
<TargetName>Config.cmake
配置<TargetName>ConfigVersion.cmake
两者任选其一。
再简单说下函数
write_basic_package_version_file(
filename # 文件名,要注意文件名配对
[VERSION major.minor.patch] # 版本号信息
COMPATIBILITY (AnyNewerVersion|SameMajorVersion) #AnyNewerVersion向后兼容,SameMajorVersion只对同一个大版本号生效
)
在其他库中引用
引用的话非常方便
首先告诉cmake库在哪个目录,有三种方法:
set(<TargetExportName>_ROOT <path>)
set(<TargetExportName>_DIR <path>)
# 设定*_DIR
会使用*_ROOT
失效set(CMAKE_PREFIX_PATH <path-to-TargetExportDir>)
# 推荐这种
然后直接查找即可
find_package(<TargetExportName> # 本例中为“myproj”
<Version> # 格式类似1.1.1.1,最长为4
REQUIRED # 找不到这个库,则停止执行cmake
)
详解输出配置的函数install(EXPORT)
第一个比较常见,这里重点讲第二个install()
函数。
install(EXPORT <export-name>
DESTINATION <dir>
[NAMESPACE <namespace>]
[FILE <name>.cmake]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[EXPORT_LINK_INTERFACE_LIBRARIES]
[COMPONENT <component>])
EXPORT
指定输出的配置target名,即在第一小节中用install(TARGETS)
指定的<export-name>
DESTINATION
配置文件放到哪个目录,建议自己指定,之后好找
NAMESPACE
指定前缀,方便识别,使用时全名是<namespace><export-name>
,如Qt5::Widgets
的namespace是Qt5::
FILE
:文件名,必须以*.cmake
结尾,如果在这些指定了路径,那么DESTINATION
可空着
PERMISSIONS
:文件访问权限
CONFIGURATIONS
:默认为空,即全部生效,如指定特定配置,那么只在特定配置生效,如指定release,则只在release时输出配置文件
EXPORT_LINK_INTERFACE_LIBRARIES
:如果使用,那么在CMP0022
是NEW
时,只有匹配(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?
的属性才会被输出
COMPONENT
:(没看明白,附上原文)If a COMPONENT option is specified that does not match that given to the targets associated with the behavior is undefined. If a library target is included in the export but a target to which it links is not included the behavior is unspecified