跨平台 CMake 项目结构示例
顶层只 include() 进来,最终暴露 IMPORTED 目标,最干净。
若系统里已经能 find_package() 到,就直接用系统包;否则走脚本里的 fallback,自己创建 IMPORTED 目标。
目录结构
MyApp/
├── CMakeLists.txt
├── cmake/
│ ├── openssl.cmake
│ ├── zlib.cmake
│ └── utils.cmake # 可选(这里没用到,可放公共宏)
└── src/
├── CMakeLists.txt
└── main.cpp
1) 顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 让 include(openssl) 之类能直接找到 cmake/ 里的脚本
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# —— 第三方库:每个脚本内部自己处理 find_package/fallback,并最终提供 IMPORTED 目标
include(openssl) # 提供 OpenSSL::SSL / OpenSSL::Crypto
include(zlib) # 提供 ZLIB::ZLIB
add_subdirectory(src)
2) cmake/openssl.cmake
# 目标:确保存在 OpenSSL::SSL 与 OpenSSL::Crypto 两个可链接目标
# 1) 先静默尝试系统包(支持 OPENSSL_ROOT 提示)
cmake_policy(PUSH)
cmake_policy(SET CMP0074 NEW) # 允许 <PackageName>_ROOT
find_package(OpenSSL QUIET)
cmake_policy(POP)
if(OpenSSL_FOUND)
message(STATUS "[3rdparty] Using system OpenSSL: ${OPENSSL_VERSION}")
# 已经有 OpenSSL::SSL / OpenSSL::Crypto,无需再做
return()
endif()
# 2) 没找到 → fallback:基于常见安装位置或用户指定根目录自行定位
message(STATUS "[3rdparty] System OpenSSL not found, using fallback")
# 允许外部传入 OPENSSL_ROOT(优先)
set(OPENSSL_ROOT "" CACHE PATH "Root directory of OpenSSL (optional)")
# 依据平台给出一些默认 hint
set(_OPENSSL_HINTS)
if(OPENSSL_ROOT)
list(APPEND _OPENSSL_HINTS "${OPENSSL_ROOT}")
endif()
if(WIN32)
# 典型预编译包路径(可按需改)
list(APPEND _OPENSSL_HINTS "C:/OpenSSL-Win64" "C:/Program Files/OpenSSL-Win64")
elseif(APPLE)
list(APPEND _OPENSSL_HINTS "/opt/homebrew/opt/openssl@3" "/usr/local/opt/openssl@3")
else() # Linux
list(APPEND _OPENSSL_HINTS "/usr" "/usr/local")
endif()
# 定位头文件与库
find_path(OPENSSL_INCLUDE_DIR
NAMES openssl/ssl.h
HINTS ${_OPENSSL_HINTS}
PATH_SUFFIXES include
REQUIRED)
find_library(OPENSSL_SSL_LIBRARY
NAMES ssl libssl
HINTS ${_OPENSSL_HINTS}
PATH_SUFFIXES lib lib64
REQUIRED)
find_library(OPENSSL_CRYPTO_LIBRARY
NAMES crypto libcrypto
HINTS ${_OPENSSL_HINTS}
PATH_SUFFIXES lib lib64
REQUIRED)
# 创建 IMPORTED 目标(最简单稳妥的 UNKNOWN + IMPORTED_LOCATION)
add_library(OpenSSL::SSL UNKNOWN IMPORTED)
set_target_properties(OpenSSL::SSL PROPERTIES
IMPORTED_LOCATION "${OPENSSL_SSL_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}"
)
add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
set_target_properties(OpenSSL::Crypto PROPERTIES
IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}"
)
# 可选:把两个库互相关联(有些平台/版本需要)
target_link_libraries(OpenSSL::SSL INTERFACE OpenSSL::Crypto)
注:若在 MSVC 下你有区分 Debug/Release 的
.lib,可另外提供
IMPORTED_LOCATION_DEBUG/IMPORTED_LOCATION_RELEASE两个属性;或直接用系统find_package(OpenSSL)。
3) cmake/zlib.cmake
# 目标:确保存在 ZLIB::ZLIB 目标
# 1) 先用系统包
find_package(ZLIB QUIET)
if(ZLIB_FOUND)
message(STATUS "[3rdparty] Using system ZLIB: ${ZLIB_VERSION}")
return()
endif()
# 2) 没找到 → fallback
message(STATUS "[3rdparty] System ZLIB not found, using fallback")
set(ZLIB_ROOT "" CACHE PATH "Root directory of zlib (optional)")
set(_ZLIB_HINTS)
if(ZLIB_ROOT)
list(APPEND _ZLIB_HINTS "${ZLIB_ROOT}")
endif()
if(WIN32)
list(APPEND _ZLIB_HINTS "C:/zlib" "C:/Program Files/zlib")
else()
list(APPEND _ZLIB_HINTS "/usr" "/usr/local" "/opt/homebrew")
endif()
find_path(ZLIB_INCLUDE_DIR
NAMES zlib.h
HINTS ${_ZLIB_HINTS}
PATH_SUFFIXES include
REQUIRED)
find_library(ZLIB_LIBRARY
NAMES z zlib
HINTS ${_ZLIB_HINTS}
PATH_SUFFIXES lib lib64
REQUIRED)
add_library(ZLIB::ZLIB UNKNOWN IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION "${ZLIB_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORY "${ZLIB_INCLUDE_DIR}" # 兼容旧拼写
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}"
)
4) src/CMakeLists.txt
add_executable(myapp main.cpp)
# 直接像系统包一样使用 IMPORTED 目标
target_link_libraries(myapp
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
ZLIB::ZLIB
)
5) src/main.cpp
#include <iostream>
#include <zlib.h>
#include <openssl/ssl.h>
int main() {
std::cout << "zlib version: " << zlibVersion() << std::endl;
#if defined(OPENSSL_VERSION_TEXT)
std::cout << "OpenSSL: " << OPENSSL_VERSION_TEXT << std::endl;
#else
std::cout << "OpenSSL present." << std::endl;
#endif
return 0;
}
使用方式
系统已有库(推荐)
cmake -S . -B build -G "Ninja Multi-Config"
cmake --build build --config Release
自定义根目录(fallback)
cmake -S . -B build -G "Ninja Multi-Config" \
-DOPENSSL_ROOT="C:/OpenSSL-Win64" \
-DZLIB_ROOT="C:/zlib"
cmake --build build --config Release
这样顶层只负责 include(openssl) / include(zlib),每个子脚本内部自己处理 find_package() 与 fallback,并把 一致的 IMPORTED 目标暴露给项目使用;新增库时只需在 cmake/ 里再加一个 xxx.cmake 即可,风格统一、无命名冲突。

浙公网安备 33010602011771号