- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
CMake 的FindOpenSSL模块用于查找系统中已安装的 OpenSSL 加密库(crypto与ssl)并确定其版本,是 C/C++ 项目集成 HTTPS、TLS 与密码学能力时最常用的入口。本文基于 Modules/FindOpenSSL.cmake 及仓库内配套测试 Tests/FindOpenSSL,完整讲解该模块的组件语义、导入目标、结果变量、搜索提示与平台适配细节,并给出可直接复制的实战用法,帮助读者彻底掌握如何在项目中可靠地发现、链接并验证 OpenSSL。
一、模块概览与基本调用形式
FindOpenSSL是一个标准的 CMake find 模块,核心功能是:
- 在系统中定位 OpenSSL 的头文件(
openssl/ssl.h)与库文件(libssl、libcrypto); - 解析并暴露 OpenSSL 的版本号;
- 生成一组可供
target_link_libraries直接使用的导入目标(Imported Targets)。
基本调用形式如下(摘自 Modules/FindOpenSSL.cmake 文档头):
find_package(OpenSSL [<version>] [COMPONENTS <components>...] [...])版本要求与版本区间
- 版本区间支持(3.20+):
find_package的版本参数除了传统的单个值(如3.20中引入的用法),还可以传入版本区间(version range),例如find_package(OpenSSL 1.1.1...3)这类语法。更详细的版本区间语义可参见find_package命令本身。 - OpenSSL 3.0 支持(3.18+):自 CMake 3.18 起,该模块正式支持 OpenSSL 3.0。这一版本在版本号宏格式上发生了重要变化(详见下文“版本解析原理”一节),模块为此实现了两套解析逻辑。
组件(COMPONENTS)
模块支持两个可选组件,二者均在 CMake 3.12 中引入:
| 组件 | 语义 |
|---|---|
Crypto | 确保找到 OpenSSL 的crypto库 |
SSL | 确保找到 OpenSSL 的ssl库 |
组件使用标准语法指定:
find_package(OpenSSL [COMPONENTS <components>...])默认行为:如果不指定任何组件,模块默认将Crypto视为必需、SSL视为可选——也就是说即使找不到ssl库,只要crypto库存在,find_package(OpenSSL)仍然算成功。若希望把SSL也变成硬性要求,必须显式写出COMPONENTS SSL。
从实现上看(Modules/FindOpenSSL.cmake),模块会遍历OpenSSL_FIND_COMPONENTS列表,对Crypto和SSL分别检查头文件与对应库是否存在,从而设置OpenSSL_<component>_FOUND;遇到未知组件则会发出WARNING并置为FALSE:
else() message(WARNING "${_comp} is not a valid OpenSSL component") set(OpenSSL_${_comp}_FOUND FALSE) endif()二、导入目标(Imported Targets)
模块在找到 OpenSSL 后提供三个导入目标(Modules/FindOpenSSL.cmake):
OpenSSL::Crypto(3.4+)
封装crypto库的使用要求,仅在找到crypto库时可用。它同时携带INTERFACE_INCLUDE_DIRECTORIES(即 OpenSSL 头文件目录),因此链接该目标后无需再手动添加包含目录。目标属性在源码中以UNKNOWN IMPORTED形式创建,并区分 DEBUG / RELEASE 配置(Modules/FindOpenSSL.cmake)。
OpenSSL::SSL(3.4+)
封装ssl库的使用要求,仅在找到ssl库时可用。为方便起见,该目标还自动链接OpenSSL::Crypto,因为ssl库本身依赖crypto库(Modules/FindOpenSSL.cmake):
if(TARGET OpenSSL::Crypto) set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) endif()因此项目中只需链接OpenSSL::SSL一个目标,crypto依赖即被自动传递。
OpenSSL::applink(3.18+)
封装 OpenSSL 应用侧接口(openssl/applink.c)的使用要求,仅在找到 OpenSSL 且版本不低于 0.9.8 时可用。该接口是 OpenSSL 的 BIO 层与 Windows 编译器运行时环境之间的“胶水层”,使用 MSVC 构建时可能需要把它编入项目。
关键注意事项:该接口文件是通过INTERFACE_SOURCES目标属性加入的。由于 CMake 中接口源文件的传播特性,官方强烈建议仅以PRIVATE作用域链接该目标,确保它在整个依赖图中只被链接一次:
target_link_libraries(project_target PRIVATE OpenSSL::applink)使用其他作用域可能引发构建或链接阶段的意外问题,因为 ISO C 与 ISO C++ 标准对链接行为的要求都非常宽松。在非 MSVC 平台上链接该目标不会产生任何效果。
实战提示:当你的 Windows + MSVC 项目与 OpenSSL 使用了不同的运行时配置(例如项目用
/MT、OpenSSL 用/MD)时,把OpenSSL::applink以PRIVATE链接进可执行文件,是官方推荐的兼容性解法。
三、结果变量(Result Variables)
模块查找完成后会定义以下变量(Modules/FindOpenSSL.cmake):
| 变量 | 含义 |
|---|---|
OpenSSL_FOUND(3.3+) | 布尔值,表示是否找到(所请求版本的)OpenSSL 库 |
OpenSSL_VERSION(4.2+) | 找到的 OpenSSL 版本,格式为<major>.<minor>.<revision><patch>,例如0.9.8s |
OPENSSL_INCLUDE_DIR | OpenSSL 头文件目录 |
OPENSSL_CRYPTO_LIBRARY | crypto库文件 |
OPENSSL_CRYPTO_LIBRARIES | crypto库及其依赖 |
OPENSSL_SSL_LIBRARY | ssl库文件 |
OPENSSL_SSL_LIBRARIES | ssl库及其依赖 |
OPENSSL_LIBRARIES | 所有 OpenSSL 库及其依赖 |
OPENSSL_APPLINK_SOURCE | OpenSSL::applink目标中的源文件;仅当 OpenSSL 版本 ≥ 0.9.8 且平台为 MSVC 时定义 |
传统变量与新变量的演进
值得注意的是OpenSSL_FOUND与OpenSSL_VERSION在文档中被同时列在“结果变量”和“弃用变量”两个小节中:
OpenSSL_FOUND(3.3 引入,4.2 起弃用):建议改用同值的OpenSSL_FOUND。OPENSSL_VERSION(4.2 起弃用):被OpenSSL_VERSION取代。
也就是说,新项目应优先使用OpenSSL_FOUND与OpenSSL_VERSION(无全大写前缀的版本),旧的全大写形式仅为向后兼容保留。此外,OPENSSL_INCLUDE_DIR与库相关变量仍保持全大写命名,这与 CMake 中 find 模块“缓存变量用大写”的传统一致,且OPENSSL_INCLUDE_DIR会被mark_as_advanced隐藏(Modules/FindOpenSSL.cmake)。
四、搜索提示变量(Hints)
模块接受以下变量来控制搜索行为(Modules/FindOpenSSL.cmake):
| 变量 | 说明 |
|---|---|
OPENSSL_ROOT_DIR | 设为某个 OpenSSL 安装的根目录,用于在自定义位置搜索库 |
OPENSSL_USE_STATIC_LIBS(3.4+) | 设为TRUE时优先选择静态 OpenSSL 库而非共享库 |
OPENSSL_MSVC_STATIC_RT(3.5+) | 设为TRUE时搜索使用 MSVC 静态运行时(MT)构建的 OpenSSL 库 |
ENV{PKG_CONFIG_PATH} | 在类 UNIX 系统上,模块使用pkg-config定位 OpenSSL;可通过设置该环境变量指定备选位置,适用于存在多套库安装的系统 |
三种提示的典型用法
# 指向自定义安装位置 set(OPENSSL_ROOT_DIR "/opt/openssl-3.0") # 优先使用静态库(便于部署单文件可执行程序) set(OPENSSL_USE_STATIC_LIBS TRUE) # MSVC 下要求链接 /MT 运行时构建的 OpenSSL set(OPENSSL_MSVC_STATIC_RT TRUE) find_package(OpenSSL REQUIRED)从实现看,OPENSSL_USE_STATIC_LIBS的工作机制是临时调整CMAKE_FIND_LIBRARY_SUFFIXES:在 MSVC 下把.lib .a排到搜索后缀之前,其他平台则只保留.a,以优先命中静态库;查找结束后再恢复原始后缀顺序(Modules/FindOpenSSL.cmake、Modules/FindOpenSSL.cmake)。而OPENSSL_MSVC_STATIC_RT则控制 MSVC 库名中的运行时后缀是MT还是MD(Modules/FindOpenSSL.cmake)。
五、平台适配与搜索路径详解
UNIX 类系统:pkg-config 优先
在 UNIX 上,模块首先尝试find_package(PkgConfig QUIET),随后执行pkg_check_modules(_OPENSSL QUIET openssl)(Modules/FindOpenSSL.cmake)。pkg-config提供的信息(包含目录、库目录、链接参数)会被并入后续的find_path/find_library提示中。因此:
- 若系统存在多套 OpenSSL(如系统自带 + Homebrew/自编译),可通过设置
PKG_CONFIG_PATH指向目标版本对应的.pc文件目录; - 也可直接用
OPENSSL_ROOT_DIR绕过 pkg-config 的默认结果。
Windows / MSVC:注册表与目录命名约定
在 MSVC 下,模块读取卸载注册表中的“Inno Setup: App Path”条目(对应 slproweb 的 Win32OpenSSL 安装包)作为搜索提示,并依据架构拼出默认安装目录(Modules/FindOpenSSL.cmake):
- 64 位:
ProgramFiles/OpenSSL-Win64、C:/OpenSSL-Win64/等; - 32 位:
ProgramFiles(x86)/OpenSSL、C:/OpenSSL/等; - ARM64:
Win64-ARM对应目录。
更重要的是,模块针对 MSVC 实现了按运行时与配置区分的库名匹配。自 OpenSSL 1.1 起,Windows 库名形如libcrypto32MTd.lib、libssl32MTd.lib,其中:
MD= 动态库 release、MDd= 动态库 debug;MT= 静态库 release、MTd= 静态库 debug。
模块分别用find_library查找 DEBUG 与 RELEASE 两套库名,再通过SelectLibraryConfigurations合并出最终的OPENSSL_CRYPTO_LIBRARY/OPENSSL_SSL_LIBRARY(Modules/FindOpenSSL.cmake)。库名还同时兼容旧版 OpenSSL 的libeay32/ssleay32命名,以及静态构建特有的_static后缀(如libcrypto_static.lib,优先级高于作为 DLL 导入库的libcrypto.lib)。
MinGW 与通用分支
- MinGW:搜索
crypto/libeay32与ssl/ssleay32,路径后缀包括lib/MinGW、lib、lib64(Modules/FindOpenSSL.cmake); - 其他平台:搜索
libcrypto/libeay32与libssl/ssleay32,路径后缀为lib(Modules/FindOpenSSL.cmake)。
特殊平台:QNX
模块对 QNX 7.0.x 做了专门处理:该系统并行提供 OpenSSL 1.0.2(头文件在usr/include/openssl,库为libcrypto.so.2/libssl.so.2)与 1.1.1(头文件在usr/include/openssl1_1,库为libcrypto1_1.so.2.1/libssl1_1.so.2.1)。当请求的版本落在 1.1 区间时,模块自动使用openssl1_1头文件后缀与1_1库名后缀(Modules/FindOpenSSL.cmake)。
静态库依赖的自动补全
链接静态 OpenSSL 时,往往还需要zlib、线程库(-pthread)与dl库。模块通过_OpenSSL_test_and_find_dependencies宏分析 pkg-config 返回的依赖库列表与链接标志(Modules/FindOpenSSL.cmake):
- 遇到
z依赖时调用find_package(ZLIB); - 遇到
-pthread标志时调用find_package(Threads); - 识别出
dl依赖(或 Linux 上兜底假设需要); - 其他无法识别的静态依赖则原样透传。
随后_OpenSSL_add_dependencies与_OpenSSL_target_add_dependencies把这些依赖追加到OPENSSL_*_LIBRARIES变量以及OpenSSL::Crypto/OpenSSL::SSL目标的INTERFACE_LINK_LIBRARIES中(Modules/FindOpenSSL.cmake)。此外,Windows 静态链接时还会自动补上ws2_32与crypt32系统库(Modules/FindOpenSSL.cmake)。
六、版本解析原理:兼容两代 OpenSSL 版本宏
模块通过读取头文件openssl/opensslv.h来解析版本(Modules/FindOpenSSL.cmake),并针对新旧两代宏做了兼容:
- 旧格式
OPENSSL_VERSION_NUMBER:十六进制编码为0xMNNFFPPS(major / minor / fix / patch / status)。模块用正则拆解出各字段,其中 patch 字段01→a、02→b……(ASCII 96 偏移换算),最终拼出如0.9.8s的版本串;patch 为00时无字母后缀。 - 新格式
OPENSSL_VERSION_STR(OpenSSL 3.0.0+):自 3.0 起新增的宏直接包含MAJOR.MINOR.PATCH文本,模块优先通过OPENSSL_VERSION_NUMBER正则匹配,若失败则回退解析OPENSSL_VERSION_STR,得到如3.0.13的版本串,并据此回填OPENSSL_VERSION_MAJOR/MINOR/FIX。
这一设计正是模块在 3.18 版本支持 OpenSSL 3.0 的关键所在。解析结果最终通过FindPackageHandleStandardArgs的VERSION_VAR参与版本校验,并支持HANDLE_VERSION_RANGE(版本区间)与HANDLE_COMPONENTS(组件语义)—— 若组件缺失或版本不满足,OpenSSL_FOUND将为FALSE(Modules/FindOpenSSL.cmake)。
七、实战示例与测试验证
官方示例
模块文档给出了两个标准用法(Modules/FindOpenSSL.cmake):
示例一:仅链接 crypto 库
find_package(OpenSSL) target_link_libraries(project_target PRIVATE OpenSSL::Crypto)示例二:显式要求 ssl 库(找不到即报错)
find_package(OpenSSL COMPONENTS SSL) target_link_libraries(project_target PRIVATE OpenSSL::SSL)更完整的工程化写法
cmake_minimum_required(VERSION 3.18) project(MySecureApp CXX) find_package(OpenSSL 1.1.1 REQUIRED COMPONENTS SSL) add_executable(my_app main.cpp) target_link_libraries(my_app PRIVATE OpenSSL::SSL)这段代码做了三件事:要求 OpenSSL ≥ 1.1.1;显式要求ssl库(连带自动传递crypto);通过导入目标自动获得头文件目录与所有平台相关依赖(线程、dl、Windows 系统库等)。
传统变量风格(兼容旧项目)
如果不使用导入目标,也可以走传统变量路径:
find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) target_link_libraries(my_app ${OPENSSL_LIBRARIES})仓库的测试项目正好验证了这两种风格是等价的。在 Tests/FindOpenSSL/rand/CMakeLists.txt 中,同一个main.cc被编译成两个可执行目标:
find_package(OpenSSL REQUIRED) add_executable(tstopensslrand_tgt main.cc) target_link_libraries(tstopensslrand_tgt OpenSSL::SSL) add_executable(tstopensslrand_var main.cc) target_link_libraries(tstopensslrand_var ${OPENSSL_LIBRARIES}) target_include_directories(tstopensslrand_var PRIVATE ${OPENSSL_INCLUDE_DIR})测试源码 Tests/FindOpenSSL/rand/main.cc 调用RAND_bytes()生成 1024 字节随机数并校验返回值,从openssl/rand.h头文件到链接目标形成完整闭环。两个可执行文件分别验证“导入目标链接”与“传统变量链接”两条路径都能正确编译、链接并运行。测试由 Tests/FindOpenSSL/CMakeLists.txt 通过ctest --build-and-test注册为FindOpenSSL.rand测试。
常见问题排查清单
Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR:这是模块的默认失败信息(Modules/FindOpenSSL.cmake)。先确认是否安装 OpenSSL 开发包(如 Debian/Ubuntu 的libssl-dev),或按提示设置OPENSSL_ROOT_DIR。- 同时存在多版本 OpenSSL:用
OPENSSL_ROOT_DIR或PKG_CONFIG_PATH明确指定目标版本。 - 静态链接报未定义符号(zlib/threads/dl):模块会自动补全,但若你的 OpenSSL 是特殊静态构建,需确认 pkg-config 元数据完整;必要时在
find_package之前先find_package(ZLIB)/find_package(Threads)。 - MSVC 运行时不一致:链接
OpenSSL::applink(PRIVATE 作用域),并视需要设置OPENSSL_MSVC_STATIC_RT以匹配/MT构建。 - 版本校验失败:检查
OpenSSL_VERSION是否满足find_package的版本/区间要求;OpenSSL 3.x 与 1.x 的解析路径不同,但最终版本号均可用于比较。
八、小结
FindOpenSSL模块在 Modules/FindOpenSSL.cmake 中实现了从搜索、版本解析到导入目标构建的完整闭环,并通过 Tests/FindOpenSSL 中的RAND_bytes用例验证了两种链接方式。核心要点可归纳为:
- 优先使用导入目标
OpenSSL::SSL(自动带出OpenSSL::Crypto)与OpenSSL::Crypto,头文件目录、依赖库均自动传播; - 组件语义:默认
Crypto必需、SSL可选,显式COMPONENTS SSL可强制要求 ssl 库; - 搜索控制:
OPENSSL_ROOT_DIR、OPENSSL_USE_STATIC_LIBS、OPENSSL_MSVC_STATIC_RT、PKG_CONFIG_PATH四个入口覆盖了绝大多数自定义安装场景; - 平台差异:UNIX 走 pkg-config,MSVC 走注册表 + 命名约定,MinGW/QNX 各有专属路径;
- 版本解析:同时兼容 OpenSSL 1.x 的
OPENSSL_VERSION_NUMBER与 3.x 的OPENSSL_VERSION_STR。
无论你是要在一个新项目中快速接入 TLS 能力,还是要排查既有构建中 OpenSSL 链接失败的疑难问题,掌握本模块的目标、变量与搜索逻辑,都能让配置过程更加可控和可预期。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
CMake FindBLAS 模块深度解析:从 find_package(BLAS) 到 BLAS::BLAS 目标
CMake FindBLAS 模块深度解析:从 find_package BLAS 到 BLAS::BLAS 目标 本篇技术指南围绕 CMake 官方仓库(本仓
构建工具开发工具CLICMake FindCups 模块深度解析:用 find_package 定位并链接 Common UNIX Printing System (CUPS)
CMake FindCups 模块深度解析:用 find_package 定位并链接 Common UNIX Printing System CUPS 本篇技术
构建工具开发工具CLICMake FindJPEG 模块深度指南:从 find_package 到 JPEG::JPEG 导入目标的完整实战
CMake FindJPEG 模块深度指南:从 find_package 到 JPEG::JPEG 导入目标的完整实战 导读 FindJPEG 是 CMake
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考