CMake快速构建安装工具、CMake快速封包脚本——PKG.cmake
你是否有过在正式写代码之前焦头烂额地编写一大堆CMake构建代码?你是否有过在封装自己的库时写了一堆install()、export()等CMake安装导出代码?你是否曾希望过自己能快速构建出能够被别人find_package得到的库?你是否曾想过自己能够快速构建出像boost那样的多组件库?那么,接下来的内容,将会让你眼前一亮,耳目一新。
Github链接
介绍
简化项目安装过程中的大量代码,使用单个函数封装一些导出和安装操作,以方便构建安装单个项目或多组件项目。
使用方法
最简单的构建安装库示例
PKG(
_NAME PKG_lib
_INCLUDE_DIRS "include/" # 此处意为“include中的所有文件或目录将被安装,但不包括include本身”
)
执行库安装后,就可以在其他项目中引用PKG_lib
库:
find_package(PKG_lib REQUIRED)
怎么样,是不是很简单?
那么我们再来看看怎么构建安装可执行文件(即不要include和lib)↓
简单的构建安装可执行文件示例
PKG(
_NAME PKG_exe
_MODE Runtime
_INSTALL_BIN_DIR "." # Windows系统适用:默认可执行文件安装在安装目录的bin目录,该代码可以使可执行文件安装到安装目录根目录上
_DISABLE_INTERFACE
_DISABLE_CONFIG
_DISABLE_VERSION
)
进行编译安装(不懂怎么使用cmake进行库编译安装的自己去百度,本篇文章不属于基础教学),你就会神奇的发现,它安装到了C:\Program Files (x86)\PKG_exe
目录中,当然这个安装目录是可以修改的,以上例子只是简单的介绍基本功能。
一个较为完整的动态库封包示例
cmake_minimum_required(VERSION 3.20)
project(PKG_shared VERSION 1.2.3.4)
add_library(PKG_shared SHARED)
add_library(PKGNS::PKG_shared ALIAS PKG_shared)
set_target_properties(
PKG_shared PROPERTIES
VERSION 1.2.3.4
#SOVERSION 1.2
DEFINE_SYMBOL "${COMPONENT_NAME}_EXPORTS"
MSVC_RUNTIME_LIBRARY "${MSVC_RUNTIME_LIB}"
)
include(cmake/PKG.cmake)
PKG(
_NAME "PKG_shared"
#_VERSION 1.2.3.4 # 因为PKG_shared已经定义了VERSION属性,这句就免了
_DEBUG_POSTFIX "d" # 相当于‘set_target_properties(PKG_shared PROPERTIES DEBUG_POSTFIX "d")’
_NAMESPACE "PKGNS"
_INCLUDE_DIRS "include/"
_INCLUDE_EXCLUDE_REG ".*\\.(svn|h\\.in|hpp\\.in)$"
_INCLUDE_DESTINATION "include/${PROJECT_NAME}-1.2.3.4"
_EXPORT_HEADER "comm1_export.h" # 相对于CMAKE_CURRENT_BINARY_DIR
_INSTALL_PDB # 仅MSVC有效
#_ADD_LIB_SUFFIX # 如果在64位计算机上,_BINARY_LIB_DIR和_INSTALL_LIB_DIR的值将添加后缀'64'
_ADD_UNINSTALL
)
target_sources(
PKG_shared PRIVATE "src/add.cpp"
)
target_include_directories(
PKG_shared PRIVATE
"include"
"${PKG_PKG_shared_EXPORT_HEADER_DIR}"
)
一个麻雀虽小五脏俱全的多组件项目封包示例
如果要打包多组件项目,此脚本将为您提供极大的便利。该功能提供两个特殊选项: _IS_COMPONENT
和_IS_COMPONENTS
。_IS_COMPONENT
指定当前_NAME
目标是项目的一个组件,则此组件将附属于_PROJECT
项目,_PROJECT
在定义_IS_COMPONENT
参数时就默认为PROJECT_NAME
。_PROJECT
可以定制。
主项目部分:
project(PKG VERSION 0.0.1)
# Its components also set this value to the default value of their _INSTALL_DIR, unless custom _INSTALL_DIR
set(PKG_PKG_INSTALL_DIR "/home/pkg/pkg_install")
add_subdirectory(component)
# Called after all child components
PKG(
_IS_COMPONENTS
_NAME ${PROJECT_NAME}
_VERSION 0.0.1
#_INCLUDE_DIRS "macro" # 在这里,您可以将其他include目录添加到 _INCLUDE_DESTINATION 指定的路径中
_INCLUDE_FILES "macro/global.h" "macro/macro.h" # 结果同上,只是这里是针对文件而不是目录
_INCLUDE_EXCLUDE_REG ".*\\.(svn|h\\.in|hpp\\.in)$"
#_INCLUDE_DESTINATION "include" # _INCLUDE_DESTINATION 的值默认为 _INSTALL_INCLUDE_DIR,也就是 "include"
_SHARED_LIBS
_ADD_UNINSTALL
)
组件部分:
add_library(component SHARED)
PKG(
_IS_COMPONENT
_NAME component
_PROJECT PKG
_NAMESPACE "${PROJECT_NAME}"
_DEBUG_POSTFIX "d"
_INCLUDE_DIRS "include/"
_INCLUDE_EXCLUDE_REG ".*\\.(svn|h\\.in|hpp\\.in)$"
_INCLUDE_DESTINATION "include/${PROJECT_NAME}"
_EXPORT_HEADER "include/component_export.h" # 将定义 PKG_PKG_component_EXPORT_HEADER_DIR 变量
_INSTALL_PDB
)
接下来,您可以在其他项目中使用find_package()
来查询该组件:
find_package(PKG COMPONENTS component REQUIRED)
...
target_link_libraries(... PRIVATE PKG::component)
...
PKG()的功能很强大,详细介绍可前往Github查看。
PKG()的代码注释
以下的内容是PKG()的开头中文注释(Github上我删掉了这部分,为了简化代码):
#===============================================================================
#
# @brief 快速打包项目或项目内的组件
#
# @par 可用参数
# _IS_COMPONENT opt,当前安装的是项目的小部件
# _IS_COMPONENTS opt,当前安装的是组件集,组件集不能是可生成目标,也就是说 `_NAME`不能是`add_library`或`add_executable`等命令生成的目标
# _NAME one,项目名称/组件名称(无默认)
# _PROJECT one,`_IS_COMPONENT` 开启时可用,指定本组件所附属的项目名称(默认:${PROJECT_NAME})
# _VERSION one,版本(默认:目标(_NAME)属性 VERSION 的值|未定义)
# _COMPATIBILITY one,定义目标的版本兼容性,
# 支持的值: `AnyNewerVersion|SameMajorVersion|SameMinorVersion|ExactVersion`(默认:AnyNewerVersion)
# _DEBUG_POSTFIX one,在Debug的编译文件的文件名后面添加标识,例如:"d",对Release无效(无默认)
# _SHARED_LIBS opt,指定函数作用域内的 BUILD_SHARED_LIBS 变量值,将会在PKG_components-config.cmake.in用到
# _BINARY_DIR one,指定项目的二进制目录(默认:${CMAKE_BINARY_DIR})
# _BINARY_BIN_DIR one,指定项目的二进制目录的 runtime 目录,相对于 `_BINARY_DIR`,也可定义绝对路径(默认:bin)
# _BINARY_LIB_DIR one,指定项目的二进制目录的 library 目录,相对于 `_BINARY_DIR`,也可定义绝对路径(默认:lib)
# _INSTALL_DIR one,指定项目的安装目录(默认:${CMAKE_INSTALL_PREFIX})
# _INSTALL_INCLUDE_DIR one,指定项目的安装目录的 include 目录,相对于 `_INSTALL_DIR`,也可定义绝对路径(默认:include)
# _INSTALL_BIN_DIR one,指定项目的安装目录的 runtime 目录,相对于 `_INSTALL_DIR`,也可定义绝对路径(默认:bin)
# _INSTALL_LIB_DIR one,指定项目的安装目录的 library 目录,相对于 `_INSTALL_DIR`,也可定义绝对路径(默认:lib)
# _ADD_LIB_SUFFIX opt,在 library 目录名添加后缀"64",仅64位系统有效
# _INCLUDE_FILES mul,目标公共标头的文件位置,可以是绝对或相对路径,相对路径相对于 `CMAKE_CURRENT_SOURCE_DIR`,支持生成器表达式(无默认)
# _INCLUDE_DIRS mul,目标公共标头的目录位置,可以是绝对或相对路径,相对路径相对于 `CMAKE_CURRENT_SOURCE_DIR`,支持生成器表达式(无默认)
# _INCLUDE_EXCLUDE_REG one,安装目标公共标头时忽略的文件或目录的完整路径相匹配的正则表达式(无默认)
# _INCLUDE_DESTINATION one,匹配目标的 `INSTALL_INTERFACE` 包含目录(默认:${_INSTALL_INCLUDE_DIR})
# _DISABLE_INTERFACE opt,禁止把 `_INCLUDE_DESTINATION` 指定的目录包含到 `INSTALL_INTERFACE` 中
# _MODE one,安装模式,支持的值: `Runtime | Development`(默认:Development)
# _NAMESPACE one,使用命名空间安装您的目标,不要添加额外的'::'(无默认)
# _EXPORT_HEADER one,此处设置创建的导出标头的文件绝对或相对路径,相对路径相对于 `CMAKE_CURRENT_BINARY_DIR`(无默认)
# _EXPORT_MACRO one,导出标头中的宏定义(默认:`${_NAME}_API|${_PROJECT}_${_NAME}_API`,将变为大写)
# _INSTALL_PDB opt,安装PDB文件,仅MSVC有效
# _DISABLE_CONFIG opt,禁用 `*-config.cmake` 文件生成
# _DISABLE_VERSION opt,始终禁用 `*-config-version.cmake` 文件生成,如果没有基于 `_VERSION` 参数值以及没有定义 `_NAME` 的属性VERSION,`*-config-version.cmake` 文件也不会生成
# _CONFIG_TEMPLATE one, 用于生成 `*-config.cmake` 文件的 config 模板文件(默认:${CMAKE_SOURCE_DIR}/cmake/PKG_normal-config.cmake.in|
# ${CMAKE_SOURCE_DIR}/cmake/PKG_components-config.cmake.in)
# _ADD_UNINSTALL opt,`_IS_COMPONENT` 关闭时可用,添加卸载命令
# _UNINSTALL_TEMPLATE one,`_IS_COMPONENT` 关闭时可用,卸载操作的模板文件(默认:${CMAKE_SOURCE_DIR}/cmake/PKG_cmake_uninstall.cmake.in)
# _UNINSTALL_ADDITIONAL mul,`_IS_COMPONENT` 关闭时可用,附加卸载的文件或目录,被附加的文件或目录将在卸载操作进行时一同进行卸载(无默认)
#
# @par 全局变量
# PKG_<PROJECT>_BINARY_DIR 如果未自定义 _BINARY_DIR 参数,则会使用改变量值作为其参数值,该值会影响附属于<PROJECT>的组件
# PKG_<PROJECT>_INSTALL_DIR 如果未自定义 _INSTALL_DIR 参数,则会使用改变量值作为其参数值,该值会影响附属于<PROJECT>的组件
#
# @par 函数导出的变量
# PKG_<_PROJECT>_<_NAME>_EXPORT_HEADER_DIR _IS_COMPONENT 开启时有效,值为导出标头的所在目录(确保 _EXPORT_HEADER 参数已定义)
# PKG_<_NAME>_EXPORT_HEADER_DIR _IS_COMPONENT 与_IS_COMPONENTS 均关闭时有效,值为导出标头的所在目录(确保 _EXPORT_HEADER 参数已定义)
其实以上的注释也足以让大家知道怎么使用该脚本,无非就是熟悉一个函数——PKG()。
结束语
PKG.cmake才是刚刚起步,有bug也在所难免,希望大家多给建议,在GitHub上多献上您的Issues,我收到信息后就会第一时间进行测试和修正,让PKG.cmake对你更有帮助。