Cmake构建模版
基础示例
# 最低 CMake 版本要求
cmake_minimum_required(VERSION 3.16)
# 定义工程名称和语言
project(mlink_device VERSION 1.0.0 LANGUAGES C)
# ============================================
# 收集源码文件
# ============================================
# 只收集 src/ 目录下的实现文件
file(GLOB_RECURSE MLINK_DEVICE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
)
# ============================================
# 创建库目标(可选静态 / 可选共享)
# ============================================
option(MLINK_DEVICE_BUILD_SHARED "Build mlink_device as shared library" ON)
if(MLINK_DEVICE_BUILD_SHARED)
add_library(mlink_device SHARED ${MLINK_DEVICE_SOURCES})
else()
add_library(mlink_device STATIC ${MLINK_DEVICE_SOURCES})
endif()
# 给库取一个 namespace 别名,让别人可以用 components::mlink_device
add_library(components::mlink_device ALIAS mlink_device)
# ============================================
# 设置 C 标准
# ============================================
target_compile_features(mlink_device PUBLIC c_std_99)
# ============================================
# PRIVATE 宏定义(不会影响其他 target)
# ============================================
target_compile_definitions(mlink_device
PRIVATE
_GNU_SOURCE
)
# ============================================
# 头文件路径设置(现代写法)
# ============================================
# PUBLIC:构建时 + 安装后供使用者使用
# PRIVATE:仅本库自己使用,不导出
target_include_directories(mlink_device
PUBLIC
# 本地构建时使用的 include 路径
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
# 安装后,头文件会被安装到 ${CMAKE_INSTALL_PREFIX}/include
# 所以使用相对路径 include(自动加上 install prefix)
$<INSTALL_INTERFACE:include>
PRIVATE
# 库内部使用的私有头文件,不暴露给外部项目
${CMAKE_CURRENT_SOURCE_DIR}/private
)
# ============================================
# 链接依赖(示例)
# ============================================
# target_link_libraries(mlink_device
# PUBLIC some_other_lib
# PRIVATE pthread
# )
# ============================================
# 安装规则(核心)
# ============================================
# 安装库文件
install(
TARGETS mlink_device
EXPORT mlink_deviceTargets # 导出 target 列表
ARCHIVE DESTINATION lib # 静态库 .a 安装位置
LIBRARY DESTINATION lib # 共享库 .so 安装位置
RUNTIME DESTINATION bin # 可执行文件(如果是 EXE)
)
# 安装公共头文件到 include/
install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
DESTINATION include
)
# ============================================
# 导出 CMake package(下游使用 find_package)
# ============================================
# 生成 mlink_deviceTargets.cmake
install(
EXPORT mlink_deviceTargets
NAMESPACE components:: # 别名前缀
DESTINATION lib/cmake/mlink_device # 包的安装位置
)
# 生成 mlink_deviceConfig.cmake & version 文件
include(CMakePackageConfigHelpers)
# 自动生成版本文件,包含 version 检查逻辑
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY ExactVersion
)
# 生成 Config 文件(下游项目 find_package 用)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/mlink_deviceConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfig.cmake
INSTALL_DESTINATION lib/cmake/mlink_device
)
# 安装 config 与 version 文件
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfigVersion.cmake
DESTINATION lib/cmake/mlink_device
)
收集源代码
file(GLOB_RECURSE MLINK_DEVICE_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/core/*.c"
)
自动递归扫描 core/ 目录下所有 .c 文件,并把结果放到变量 MLINK_DEVICE_SOURCES 中。
创建库目标
option(MLINK_DEVICE_BUILD_SHARED "Build mlink_device as shared library" ON)
if(MLINK_DEVICE_BUILD_SHARED)
add_library(mlink_device SHARED ${MLINK_DEVICE_SOURCES})
else()
add_library(mlink_device STATIC ${MLINK_DEVICE_SOURCES})
endif()
# 给库取一个 namespace 别名,让别人可以用 components::mlink_device
add_library(components::mlink_device ALIAS mlink_device)
- option:添加一个Bool的开关,决定是编译动态库还是静态库。
- add_library:创建一个库,语法add_library(name [STATIC | SHARED | MODULE] [sources…])
ALIAS是给库创建一个新的别名,方便其他模块调用。
编译参数
target_compile_features(mlink_device PUBLIC c_std_99)
# ========
# PRIVATE 宏定义(不会影响其他 target)
# ========
target_compile_definitions(mlink_device
PRIVATE
_GNU_SOURCE
)
- target_compile_features:如上示例,gcc -std=c99
- target_compile_definitions:添加编译宏,-Dxxx,如上-D_GNU_SOURCE,但PRIVATE设置了作用于只对taget自己游侠,下游不继承。
作用域
| 设置方式 | A 本身用 | A 的用户(B)用 |
|---|---|---|
| PRIVATE | ✔ | ✘ |
| PUBLIC | ✔ | ✔(传播) |
| INTERFACE | ✘ | ✔(只传播) |
头文件搜索
target_include_directories(mlink_device
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include/mcp
${CMAKE_CURRENT_SOURCE_DIR}/include/transport
${CMAKE_CURRENT_SOURCE_DIR}/include/utils
)
- target_include_directories:给目标mlink_device配置编译时要搜索的头文件目录,类似加上-I….。
- PRIVATE:在编译mlink_device模块编译自己这个库时寻找的头文件路径,不会传给它的依赖目标。
- PUBLIC:这是mlink_device对外提供API的头文件目录,其他模块链接mlink_device库时也会自动继承这些include目录。
PRIVATE搜索的头文件路径不会传播,只会影响当前的mlink_device模块这个编译目标。而PUBLIC会传播给他一来的target,然后这个传播又分为两类一个是构建期和安装后。
- PUBLIC + BUILD_INTERFACE:构建阶段,适用于同一个构建Cmake,提供的头文件路径为源码路径,${CMAKE_CURRENT_SOURCE_DIR}/include就是srobot/components/mlink/include。
- PUBLIC + INSTALL_INTERFACE:安装阶段,适用于不同的构建Cmake,给其他模块find_package() 后使用的 include 路径。:include就会变成 ${PREFIX}/include,如srobot/outpu/sdk/include。
库搜索
find_package+target_link_libraries组合。
find_package(OpenSSL REQUIRED)
Cmake就会自动搜索
/usr/lib/cmake/openssl/...
/usr/local/lib/cmake/openssl/...
/opt/sdk/lib/cmake/openssl/...
然后只需要:
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
库和头文件导出
#导出编译产物如库、可执行文件
install(
TARGETS mlink_device
EXPORT ComponentsTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
#导出普通文件,如头文件
install(
FILES
${CMAKE_CURRENT_SOURCE_DIR}/include/mlink.h
DESTINATION include
)
- EXPORT:mlink_device 这个 target 加入一个“targets 导出列表”,后面install(EXPORT…) 把它写入 CMake 模块文件,让外部项目 find_package 时导入 components::mlink_device。
- ARCHIVE DESTINATION lib:静态库(.a)安装到哪里?——> prefix/lib。编译cmake指定的-DCMAKE_INSTALL_PREFIX
- LIBRARY DESTINATION lib:共享库(.so / .dylib)安装到哪里?——>prefix/lib。
- RUNTIME DESTINATION bin:可执行文件安装到哪里?——>prefix/bin。
- FILES:导出头文件到——>prefix/include