CMake Find_Package和Install

代码: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模式下,查找到相应的库后一般会设置以下变量:

  1. <PackageName>_FOUND

  2. <PackageName>_INCLUDE_DIR 或 <PackageName>_INCLUDES

  3. <PackageName>_LIBRARY 或 <PackageName>_LIBRARIES

对于Config模式通常找到的是一个Target,具体Target的名字库中的CMake决定。

对于一些库配置比较麻烦,可以支持通过参数传递-D<PackageName>_LIBRARY="..."和-D<PackageName>_INCLUDE_DIR="...",可以让find_package返回正确的值。

Module模式

MODULE模式中cmake通过查找名为Find<PackageName>.cmake的文件来查找包。查找顺序如下:

  1. 首先在CMAKE_MODULE_PATH变量中查找,可以设置多个目录用分号分隔。

  2. 在cmake安装目录下的“share\cmake-[version]\Modules”查找

  3. 以上都没有找到则转入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的配置问题,查找顺序如下:

  1. 查找<PackageName>_ROOT变量或者<PackageName>_ROOT环境变量

  2. 查找CMAKE_PREFIX_PATH变量,如果设置多个目录用分号分隔。

  3. 查找<PackageName>_DIR变量或环境变量<PackageName>_DIR 或者 CMAKE_PREFIX_PATH

  4. 查找HINTS 选项指定的目录。

  5. 在环境变量Path中查找,如果Path中的路径以bin或sbin,搜索时会去掉bin或sbin。

  6. 查找 CMake 用户包注册表中路径,windows下注册表HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\<PackageName>

  7. 查找cmake的系统变量CMAKE_SYSTEM_PREFIX_PATH中的路径

  8. 查找cmake系统注册表,windows下注册表HKEY_LOCAL_MACHINE\Software\Kitware\CMake\Packages\<PackageName>

  9. 查找PATHS参数指定的目录

对于以上每一个目录,会查找以下子目录,W为Windows支持,U为Linux/Unix支持,

其中prefix是查找目录,name是包名或者参数NAMES中指定的名字

通常用法

  1. 程序编译时指定CMAKE_INSTALL_PREFIX="C:/cmake-pkg/<PackageName>"

  2. 编译时使用-DCMAKE_PREFIX_PATH="C:/cmake-pkg/"参数

对应一些cmake内置的模块(Modules目录),需要特别注意,例如ZLIB,当查找ZLIB时需要提供ZLIB_ROOT="C:/cmake-pkg/ZLIB"参数指定ZLIB的安装目录。

Install命令

使用

  1. 生成cmake项目时需要加上参数CMAKE_INSTALL_PREFIX="C:/cmake-pkg/<PackageName>"设置好安装目录

  2. cmake --build [build_dir] --config="debug|release"

  3. 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引用。

使用步骤如下:

  1. 使用<PackageName>_FOUND确定是否找到库

  2. target_link_libraries 直接使用 命名空间+库名直接引用。

  3. 无需设置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 ()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值