代码:https://gitee.com/Kyle12/StudyProject/tree/master/cmake/install
Find_Package
find_package(<PackageName> [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[MODULE|CONFIG|NO_MODULE]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)])
参考:find_package — CMake 3.29.2 Documentation
Find_Package分为两种模式,一种是Module模式一种是Config模式,只要找到对应的配置文件find_package就会设置<PackageName>_FOUND=1。
对于Module模式下,查找到相应的库后一般会设置以下变量:
-
<PackageName>_FOUND
-
<PackageName>_INCLUDE_DIR 或 <PackageName>_INCLUDES
-
<PackageName>_LIBRARY 或 <PackageName>_LIBRARIES
对于Config模式通常找到的是一个Target,具体Target的名字库中的CMake决定。
对于一些库配置比较麻烦,可以支持通过参数传递-D<PackageName>_LIBRARY="..."和-D<PackageName>_INCLUDE_DIR="...",可以让find_package返回正确的值。
Module模式
MODULE模式中cmake通过查找名为Find<PackageName>.cmake的文件来查找包。查找顺序如下:
-
首先在CMAKE_MODULE_PATH变量中查找,可以设置多个目录用分号分隔。
-
在cmake安装目录下的“share\cmake-[version]\Modules”查找
-
以上都没有找到则转入Config模式。
module主要用于没有提供共Cmake配置的库,这种情况可以为库也一个配置,格式大致如下:
set(LibPath "${CMAKE_CURRENT_LIST_DIR}/../install/libShared")
find_path(
SHARED_INCLUDE_DIR
NAMES LibShared.hpp
PATHS ${LibPath}
NO_DEFAULT_PATH
)
find_library(
SHARED_LIB_DEBUG
NAMES "libSharedd.lib"
PATHS "${LibPath}"
NO_DEFAULT_PATH
)
find_library(
SHARED_LIB_RELEASE
NAMES "libShared.lib"
PATHS "${LibPath}"
NO_DEFAULT_PATH
)
message(STATUS "LibPath:${LibPath}")
message(STATUS "SHARED_INCLUDE_DIR:${SHARED_INCLUDE_DIR}")
message(STATUS "SHARED_LIB_DEBUG:${SHARED_LIB_DEBUG}")
message(STATUS "SHARED_LIB_RELEASE:${SHARED_LIB_RELEASE}")
if(NOT TARGET MyLibShared)
# 将目标设置为在项目中全局可见 GLOBAL
add_library(MyLibShared SHARED IMPORTED GLOBAL)
# 为导入目标添加相关属性
set_target_properties(MyLibShared PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${SHARED_INCLUDE_DIR}"
# IMPORTED_LOCATION "libShared.so" #linux 需要使用IMPORTED_LOCATION属性
IMPORTED_IMPLIB "${SHARED_LIB_RELEASE}"
IMPORTED_IMPLIB_DEBUG "${SHARED_LIB_DEBUG}"
IMPORTED_IMPLIB_RELEASE "${SHARED_LIB_RELEASE}"
MAP_IMPORTED_CONFIG_MINSIZEREL Release
MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release
)
endif()
#set(SHARED_LIBS ${SHARED_LIB_DEBUG} ${SHARED_LIB_RELEASE})
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SHARED_LIBS ${SHARED_LIB_DEBUG})
else ()
set(SHARED_LIBS ${SHARED_LIB_RELEASE})
endif ()
# 这里用到了一个 CMake 自带的函数,会根据指定内容设置 MyLibShared_FOUND 变量
include(FindPackageHandleStandardArgs)
# 如果 SHARED_LIBS或SHARED_INCLUDE_DIR 为空,则 MyLibShared_FOUND 将被设置为 FALSE,并在使用 find_package(LibShared REQUIRED) 时也会报错
# 否则MyLibShared_FOUND设置为 TRUE
find_package_handle_standard_args(
MyLibShared
REQUIRED_VARS SHARED_LIBS SHARED_INCLUDE_DIR
)
Config模式
在CONFIG模式中查找<lowercasePackageName>-config.cmake或者<PackageName>Config.cmake的配置问题,查找顺序如下:
-
查找<PackageName>_ROOT变量或者<PackageName>_ROOT环境变量
-
查找CMAKE_PREFIX_PATH变量,如果设置多个目录用分号分隔。
-
查找<PackageName>_DIR变量或环境变量<PackageName>_DIR 或者 CMAKE_PREFIX_PATH
-
查找HINTS 选项指定的目录。
-
在环境变量Path中查找,如果Path中的路径以bin或sbin,搜索时会去掉bin或sbin。
-
查找 CMake 用户包注册表中路径,windows下注册表HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\<PackageName>
-
查找cmake的系统变量CMAKE_SYSTEM_PREFIX_PATH中的路径
-
查找cmake系统注册表,windows下注册表HKEY_LOCAL_MACHINE\Software\Kitware\CMake\Packages\<PackageName>
-
查找PATHS参数指定的目录
对于以上每一个目录,会查找以下子目录,W为Windows支持,U为Linux/Unix支持,
其中prefix是查找目录,name是包名或者参数NAMES中指定的名字
通常用法
-
程序编译时指定CMAKE_INSTALL_PREFIX="C:/cmake-pkg/<PackageName>"
-
编译时使用-DCMAKE_PREFIX_PATH="C:/cmake-pkg/"参数
对应一些cmake内置的模块(Modules目录),需要特别注意,例如ZLIB,当查找ZLIB时需要提供ZLIB_ROOT="C:/cmake-pkg/ZLIB"参数指定ZLIB的安装目录。
Install命令
使用
-
生成cmake项目时需要加上参数CMAKE_INSTALL_PREFIX="C:/cmake-pkg/<PackageName>"设置好安装目录
-
cmake --build [build_dir] --config="debug|release"
-
Cmake --install [build_dir]
创建
官方参考: Step 5: Installing and Testing — CMake 3.29.2 Documentation
install — CMake 3.29.2 Documentation
1. 生成模块Moudule,一个模块中可以包含多个目标(库、可执行文件)。
# 安装 libA 库 到${ModulesName}
install(TARGETS libA
EXPORT ${ModulesName}
LIBRARY DESTINATION "${ModulesDir}/lib"
ARCHIVE DESTINATION "${ModulesDir}/lib"
RUNTIME DESTINATION "${ModulesDir}/bin"
COMPONENT libA
)
# 安装 libB 库 到${ModulesName}
install(TARGETS libB
EXPORT ${ModulesName}
LIBRARY DESTINATION ${ModulesDir}/lib
ARCHIVE DESTINATION ${ModulesDir}/lib
RUNTIME DESTINATION ${ModulesDir}/bin
PUBLIC_HEADER DESTINATION "${ModulesDir}/include" # 不能保证目录结构
)
# 安装 ${ModulesName} 到文件 ${ModulesName}.cmake
install(
EXPORT ${ModulesName}
FILE ${ModulesName}.cmake
DESTINATION "${ModulesCMakeDir}" # 一般安装路径会设置在此处
NAMESPACE My::
COMPONENT ${ModulesName}
)
# 安装 头文件
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
DESTINATION "${ModulesDir}/include"
COMPONENT libA
FILES_MATCHING
PATTERN "*.hpp"
PATTERN "*.h"
PATTERN "*.cpp" EXCLUDE
# REGEX "(.hpp)|(.h)$" #使用正则表达式匹配
)
2. 生成对应的Config文件
set(LIB_COMPONENTS libA libB)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LIB_SUFFIX "d")
else ()
set(LIB_SUFFIX "")
endif ()
get_filename_component(INSTALL_PATH "${CMAKE_CURRENT_LIST_DIR}/../../../" REALPATH)
message(STATUS "MyModule INSTALL_PATH:${INSTALL_PATH}")
set(MyModuleIncludes)
set(MyModuleLibs)
foreach(component ${LIB_COMPONENTS})
set(includeFileName "${INSTALL_PATH}/include/${component}.hpp")
set(libFileName "${INSTALL_PATH}/lib/${component}${LIB_SUFFIX}.lib")
if(NOT EXISTS "${includeFileName}")
message(WARNING "MyModule: Include file doesn't exist: '${includeFileName}'. MyModule installation may be broken. Skip...")
else()
list(APPEND MyModuleIncludes ${includeFileName})
endif()
if(NOT EXISTS "${libFileName}")
message(WARNING "MyModule: library file doesn't exist: '${libFileName}'. MyModule installation may be broken. Skip...")
else()
list(APPEND MyModuleLibs ${libFileName})
endif()
unset(includeFileName)
unset(libFileName)
endforeach()
# ======================================================
# 库文件搜索
# ======================================================
# 包含导出配置的 *.cmake 文件
include(${CMAKE_CURRENT_LIST_DIR}/MyModule.cmake)
add_library(MyModule INTERFACE)
foreach(component ${LIB_COMPONENTS})
target_link_libraries(MyModule INTERFACE My::${component}) # My::是install(EXPORT...)设置的NAMESPACE
# MINSIZEREL、RELWITHDEBINFO 使用Release的库
# set_target_properties(My::${component} PROPERTIES
# MAP_IMPORTED_CONFIG_MINSIZEREL Release
# MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release
# )
endforeach()
# ======================================================
# 搜寻的结果、状态
# ======================================================
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MyModule
REQUIRED_VARS MyModuleLibs MyModuleIncludes
)
3. 生成版本信息,并使用install FILES 安装版本文件和Config文件
set(VERSION_MAJOR "1")
set(VERSION_MINOR "2")
set(VERSION_PATCH "0")
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${ModulesVersionFile}
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}
COMPATIBILITY SameMajorVersion)
install(FILES
${ModulesVersionFile}
${CMAKE_CURRENT_SOURCE_DIR}/MyModuleConfig.cmake
DESTINATION "${ModulesCMakeDir}"
COMPONENT ${ModulesName})
add_custom_target(${ModulesName}Install ALL COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --config $<CONFIG>)
引用
这种自动生成的是Config模式,find_package可以找到,只会设置<PackageName>_FOUND变量,以及一个命名空间+库名的Target,这个Target可以直接用target_link_libraries引用。
使用步骤如下:
-
使用<PackageName>_FOUND确定是否找到库
-
target_link_libraries 直接使用 命名空间+库名直接引用。
-
无需设置include目录,因为返回的库并不是文件名,而是一个Target,这个Target已经设置过了头文件目录。
代码:
find_package(MyModule CONFIG)
if (MyModule_FOUND)
target_link_libraries(${target_name} PUBLIC MyModule)
else ()
message(WARNING "Can't Find MyModule!!")
endif ()