CMake导出及安装包模板
代码:melonedo/cmake-lecture/export
生成库
目录结构:
eval_add
├──CMakeLists.txt
├──cmake
│ ├──installer.cmake
│ └──EvalAddConfig.cmake
├──include
│ └──eval_add
│ └──eval_add.h
└──source
└──eval_add.cpp
为了保证安装目录/usr/include头文件不会重名,可以给自己的项目单独建一个文件夹。这也反应在了目录结构中,即include下是eval_add,之后才是头文件。
这里简单写一个依赖boost的库,头文件没有包含boost相关,所以Boost::boost
也是private。
### eval_add/CMakeLists.txt ###
cmake_minimum_required(VERSION 3.13)
# 少写个C语言省点配置的时间
project(eval_add VERSION 0.0.1 LANGUAGES CXX)
# 提供CMAKE_INSTALL_INCLUDEDIR
include(GNUInstallDirs)
# 外部依赖
find_package(Boost REQUIRED COMPONENTS regex)
# 定义库,没什么特别的
add_library(eval_add SHARED source/eval_add.cpp)
target_link_libraries(eval_add PRIVATE Boost::dynamic_linking Boost::boost Boost::regex Boost::diagnostic_definitions)
if(MSVC)
target_compile_options(eval_add PRIVATE $<BUILD_INTERFACE:/utf-8>)
endif()
# 需要区分编译和安装,安装后包含路径只是一个简单的include
target_include_directories(eval_add
PUBLIC
$<BUILD_INTERFACE:${eval_add_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
set_target_properties(eval_add PROPERTIES
CXX_STANDARD 11
WINDOWS_EXPORT_ALL_SYMBOLS ON # 懒得写__declspec
DEBUG_POSTFIX -debug # 添加一个后缀区分不同的configuration
)
# 见下
include(cmake/installer.cmake)
导出库
为了让别人使用自己的库,需要把当前的目标的依赖和各种编译要求导出到文件中,随着二进制文件和头文件一起打包。需要安装的内容包括:
- 本项目的CMake文件XXXConfig.cmake。
- 本项目的版本XXXConfigVersion.cmake。
- 导出的目标的二进制文件。
- 导出的目标的说明XXXTargets.cmake。实际上会根据configuration再生成一个XXXTargets-Relase/Debug.cmake。
- 导出的变量写在XXXConfig.cmake。
- 其他文件,如头文件、版权许可。
### eval_add/cmake/installer.cmake ###
include(GNUInstallDirs)
# 版本号为项目版本,生成版本文件
include(CMakePackageConfigHelpers)
write_basic_package_version_file(EvalAddConfigVersion.cmake
VERSION ${eval_add_VERSION}
COMPATIBILITY SameMajorVersion)
# 安装XXXConfig.cmake(见下)和对应的版本文件
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/cmake/EvalAddConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/EvalAddConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/EvalAdd)
# 安装生成的二进制文件,用默认配置即可
install(TARGETS eval_add EXPORT EvalAddTargets)
# 安装本项目的目标
install(EXPORT EvalAddTargets
FILE EvalAddTargets.cmake
NAMESPACE EvalAdd:: # 命名空间
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/EvalAdd)
# 安装头文件
install(DIRECTORY include/eval_add TYPE INCLUDE)
其中安装头文件的地方也可以通过设置目标的PUBLIC_HEADER
属性实现,不过看起来似乎设计上不是通用的。
这个库的Config.cmake需要手写,内容是找到依赖以及包含上面的目标
### eval_add/cmake/EvalAddConfig.cmake ###
include(CMakeFindDependencyMacro)
# 似乎不需要写REQUIRED
find_dependency(Boost COMPONENTS regex)
include("${CMAKE_CURRENT_LIST_DIR}/EvalAddTargets.cmake")
导出变量
如果要导出变量,为了保证不出现绝对路径,需要使用configure_package_config_file
来处理相对路径。首先写模板:
### eval_add/cmake/EvalAddConfig.cmake.in ###
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Boost COMPONENTS regex)
include("${CMAKE_CURRENT_LIST_DIR}/EvalAddTargets.cmake")
set_and_check(EvalAdd_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
check_required_components(EvalAdd)
为了把上面@PACKAGE_var@
替换成var
相对安装目录的路径,installer.cmake
中不能直接用install(FILES)
安装cmake/EvalAddConfig.cmake
,而是需要先配置
configure_package_config_file(cmake/EvalAddConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/EvalAddConfig.cmake # 和下面保持一致
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/EvalAdd # 和下面保持一致
PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/EvalAddConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/EvalAdd)
命令
cmake -B build -DCMAKE_INSTALL_PREFIX=/eval_add/root -DBOOST_ROOT=/boost/root
cmake --build build --target install
使用库
# $<TARGET_RUNTIME_DLLS:target>需要3.21
cmake_minimum_required(VERSION 3.21 REQUIRED)
project(use_eval_add VERSION 0.0.1 LANGUAGES CXX)
# 找到上面的库,注意此时Boost也需要找到,同样要设置BOOST_ROOT等变量
find_package(EvalAdd REQUIRED)
add_executable(main main.cpp)
# 这样就自动处理好了所有的间接依赖、包含路径、编译选项等
target_link_libraries(main EvalAdd::eval_add)
## 接下来是安装可执行文件
# 如果不执行后面的安装,把dll复制到生成目录,方便调试
add_custom_command(TARGET main POST_BUILD COMMAND_EXPAND_LISTS
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:main> $<TARGET_FILE_DIR:main>)
# 提供一个直接运行程序的方法:cmake --build build --target run-main
add_custom_target(run-main main)
# 安装应用
include(GNUInstallDirs)
# 默认就好
install(TARGETS main)
# 不确定怎么做,linux上设置一下RPATH似乎好点
set_target_properties(main PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR})
# 同样,可以复制dll到安装目录
install(FILES $<TARGET_RUNTIME_DLLS:main> TYPE BIN)
命令
cmake -B build -DEvalAdd_ROOT=/eval_add/root -DBOOST_ROOT=/boost/root
cmake --build build --target run-main
cmake --build build --target install