CMake中find_package为何在配置阶段查找预编译库?
我的项目依赖Libcurl库,Libcurl又依赖MBedTLS库。我在CMake中用add_subdirectory引入curl后,curl的CMake脚本会执行这段逻辑:
if(CURL_USE_MBEDTLS) find_package(MbedTLS REQUIRED) endif()
这会触发FindMbedTLS.cmake,里面包含这段查找逻辑:
find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h") find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto")
我能理解find_path查找头文件的逻辑,但find_library是找库文件——现在明明还在CMake配置阶段,MBedTLS的库都还没编译出来呢。我搞不懂:
find_package在配置阶段执行,为什么要去查找预编译好的库?- 就算我先通过
add_subdirectory引入MBedTLS,能拿到头文件路径,但库还没构建,怎么可能获取到库文件路径?
为什么find_package会查找预编译库?
find_package的设计覆盖两种典型场景:一是查找系统已安装的预编译依赖库,二是识别通过add_subdirectory引入的源码库。但传统的FindXXX.cmake脚本(比如这里的FindMbedTLS.cmake)大多是为第一种场景设计的——默认优先寻找系统中已存在的预编译库,毕竟很多项目会直接使用系统包管理器安装的依赖。
先引入MBedTLS源码时的解决办法
如果你先通过add_subdirectory引入MBedTLS源码,MBedTLS的CMake脚本会自动注册导入目标(例如MbedTLS::mbedtls、MbedTLS::mbedx509这类)。如果FindMbedTLS.cmake符合现代CMake规范,它会优先检测这些已存在的目标,不会再调用find_library去查找预编译库文件。
要是碰到老旧的FindMbedTLS.cmake,它可能只会执着于查找文件路径,这时候你可以手动设置FindMbedTLS.cmake需要的变量,直接指向MBedTLS源码构建的目标:
# 先引入MBedTLS源码 add_subdirectory(path/to/mbedtls) # 手动设置变量,跳过find_library的查找逻辑 set(MBEDTLS_INCLUDE_DIR ${mbedtls_SOURCE_DIR}/include) set(MBEDTLS_LIBRARY MbedTLS::mbedtls) set(MBEDX509_LIBRARY MbedTLS::mbedx509) set(MBEDCRYPTO_LIBRARY MbedTLS::mbedcrypto) # 再引入Libcurl add_subdirectory(path/to/curl)
这样curl的find_package(MbedTLS REQUIRED)就会直接使用你设置的变量,不会再去查找还未编译生成的库文件。
核心逻辑
CMake配置阶段只需要获取依赖的接口信息(头文件路径、编译选项、链接目标标识),不需要拿到实际的库文件路径。如果是通过源码引入的依赖,CMake会自动跟踪后续构建阶段生成的库文件,配置阶段只需要知道目标名称即可——这也是现代CMake推荐使用导入目标而非直接传递文件路径的原因,可靠性更高。
内容的提问来源于stack exchange,提问作者Zebrafish

