7、cmake文件类型详解
约 3073 字大约 10 分钟
2025-04-26
.cmake 文件是 CMake 构建系统的模块文件或脚本文件,包含 CMake 语言编写的代码。它们用于模块化、重用和组织 CMake 代码。
目录
- .cmake 文件类型
- 模块文件 (Modules)
- 工具链文件 (Toolchain Files)
- 脚本文件 (Scripts)
- 配置文件 (Config Files)
- 查找模块 (Find Modules)
- 如何使用 .cmake 文件
- 编写自己的 .cmake 文件
- 完整示例
- 最佳实践
1. .cmake 文件类型
CMake 使用多种类型的 .cmake 文件,每种都有不同的用途:
| 类型 | 文件名模式 | 用途 |
|---|---|---|
| 模块文件 | *.cmake | 可重用的 CMake 代码模块 |
| 工具链文件 | *.cmake | 定义交叉编译工具链 |
| 脚本文件 | *.cmake | 独立执行的 CMake 脚本 |
| 配置文件 | *Config.cmake | 为库提供配置信息 |
| 查找模块 | Find*.cmake | 查找外部库和包 |
| 包配置文件 | *-config.cmake | 已安装包的配置文件 |
2. 模块文件 (Modules)
2.1 什么是模块文件
模块文件包含可重用的 CMake 代码,可以在多个项目中通过 include() 命令使用。
Utils.cmake:
# Utils.cmake - 通用工具函数模块
# 打印带颜色的信息
function(message_color COLOR MESSAGE)
if(CMAKE_VERSION VERSION_LESS "3.20")
message(STATUS "${MESSAGE}")
else()
if(COLOR STREQUAL "RED")
message(STATUS "\033[31m${MESSAGE}\033[0m")
elseif(COLOR STREQUAL "GREEN")
message(STATUS "\033[32m${MESSAGE}\033[0m")
elseif(COLOR STREQUAL "YELLOW")
message(STATUS "\033[33m${MESSAGE}\033[0m")
else()
message(STATUS "${MESSAGE}")
endif()
endif()
endfunction()
# 检查编译器版本
function(check_compiler_version MIN_VERSION)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MIN_VERSION)
message(FATAL_ERROR "GCC 版本必须 >= ${MIN_VERSION}")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MIN_VERSION)
message(FATAL_ERROR "Clang 版本必须 >= ${MIN_VERSION}")
endif()
endif()
endfunction()
# 添加测试目标
function(add_test_target TARGET_NAME SOURCE_FILE)
add_executable(${TARGET_NAME} ${SOURCE_FILE})
target_link_libraries(${TARGET_NAME} PRIVATE GTest::gtest_main)
add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME})
message_color("GREEN" "测试目标 ${TARGET_NAME} 已添加")
endfunction()2.2 使用模块文件
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject)
# 设置模块路径
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# 包含模块
include(Utils)
# 使用模块中的函数
check_compiler_version("7.0")
add_executable(myapp main.cpp)
if(BUILD_TESTS)
enable_testing()
find_package(GTest REQUIRED)
add_test_target(test_math test_math.cpp)
endif()3. 工具链文件 (Toolchain Files)
3.1 什么是工具链文件
工具链文件用于交叉编译,定义编译器、链接器、目标系统等信息。
arm-linux-gnueabihf.cmake:
# arm-linux-gnueabihf.cmake - ARM 交叉编译工具链
# 目标系统名称
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# 编译器
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# 查找根路径
set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf)
# 查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# 编译器标志
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=neon" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=neon" CACHE STRING "" FORCE)mingw-w64.cmake:
# mingw-w64.cmake - Windows 交叉编译工具链
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)3.2 使用工具链文件
# 使用工具链文件配置
cmake -DCMAKE_TOOLCHAIN_FILE=cmake/arm-linux-gnueabihf.cmake -B build-arm -S .
# 构建
cmake --build build-arm4. 脚本文件 (Scripts)
4.1 什么是脚本文件
脚本文件是独立的 CMake 脚本,可以通过 cmake -P 执行,用于自动化任务。
generate_version.cmake:
# generate_version.cmake - 生成版本头文件的脚本
cmake_minimum_required(VERSION 3.10)
# 参数
set(OUTPUT_FILE "version.h" CACHE STRING "输出文件路径")
set(VERSION "1.0.0" CACHE STRING "版本号")
# 解析版本号
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" _ ${VERSION})
set(MAJOR ${CMAKE_MATCH_1})
set(MINOR ${CMAKE_MATCH_2})
set(PATCH ${CMAKE_MATCH_3})
# 生成头文件
file(WRITE ${OUTPUT_FILE} "// 自动生成的文件,请勿手动修改\n\n")
file(APPEND ${OUTPUT_FILE} "#ifndef VERSION_H\n")
file(APPEND ${OUTPUT_FILE} "#define VERSION_H\n\n")
file(APPEND ${OUTPUT_FILE} "#define VERSION_MAJOR ${MAJOR}\n")
file(APPEND ${OUTPUT_FILE} "#define VERSION_MINOR ${MINOR}\n")
file(APPEND ${OUTPUT_FILE} "#define VERSION_PATCH ${PATCH}\n")
file(APPEND ${OUTPUT_FILE} "#define VERSION_STRING \"${VERSION}\"\n\n")
file(APPEND ${OUTPUT_FILE} "#endif // VERSION_H\n")
message(STATUS "版本头文件已生成: ${OUTPUT_FILE}")run_tests.cmake:
# run_tests.cmake - 运行测试的脚本
cmake_minimum_required(VERSION 3.10)
set(TEST_DIR "tests" CACHE STRING "测试目录")
set(REPORT_DIR "reports" CACHE STRING "报告目录")
# 创建报告目录
file(MAKE_DIRECTORY ${REPORT_DIR})
# 查找测试可执行文件
file(GLOB TEST_EXECUTABLES "${TEST_DIR}/*_test")
# 运行每个测试
foreach(TEST ${TEST_EXECUTABLES})
get_filename_component(TEST_NAME ${TEST} NAME)
message(STATUS "运行测试: ${TEST_NAME}")
execute_process(
COMMAND ${TEST}
OUTPUT_FILE ${REPORT_DIR}/${TEST_NAME}.out
ERROR_FILE ${REPORT_DIR}/${TEST_NAME}.err
RESULT_VARIABLE RESULT
)
if(RESULT EQUAL 0)
message(STATUS "测试 ${TEST_NAME} 通过")
else()
message(WARNING "测试 ${TEST_NAME} 失败: ${RESULT}")
endif()
endforeach()4.2 执行脚本
# 执行生成版本脚本
cmake -DOUTPUT_FILE=src/version.h -DVERSION=2.1.3 -P cmake/generate_version.cmake
# 执行测试脚本
cmake -DTEST_DIR=build/tests -DREPORT_DIR=test_reports -P cmake/run_tests.cmake5. 配置文件 (Config Files)
5.1 库配置文件
库安装时生成的配置文件,供 find_package() 使用。
MyLibConfig.cmake.in:
# MyLibConfig.cmake.in - 库配置文件模板
@PACKAGE_INIT@
# 包含目标导出文件
include("${CMAKE_CURRENT_LIST_DIR}/MyLibTargets.cmake")
# 检查必需的组件
set(MyLib_FOUND TRUE)
foreach(component ${MyLib_FIND_COMPONENTS})
if(component STREQUAL "Core")
# 核心组件总是可用
elseif(component STREQUAL "Extra")
if(NOT TARGET MyLib::Extra)
set(MyLib_FOUND FALSE)
set(MyLib_MISSING_COMPONENTS "${MyLib_MISSING_COMPONENTS} ${component}")
endif()
endif()
endforeach()
# 检查必需的组件
check_required_components(MyLib)
# 版本信息
set(MyLib_VERSION @PROJECT_VERSION@)
# 编译选项
set(MyLib_CXX_FLAGS "@CMAKE_CXX_FLAGS@")5.2 生成配置文件
# CMakeLists.txt 中的配置
include(CMakePackageConfigHelpers)
# 生成配置文件
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/MyLibConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake
INSTALL_DESTINATION lib/cmake/MyLib
)
# 生成版本文件
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
# 安装配置文件
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
DESTINATION lib/cmake/MyLib
)6. 查找模块 (Find Modules)
6.1 什么是查找模块
查找模块用于定位系统中安装的库,文件名格式为 Find<PackageName>.cmake。
FindMyLib.cmake:
# FindMyLib.cmake - 查找 MyLib 库
#[=======================================================================
FindMyLib
----------
查找 MyLib 库的 CMake 模块。
导入的目标
^^^^^^^^^^
如果找到,这个模块会创建以下导入目标:
MyLib::MyLib - 主库目标
结果变量
^^^^^^^^^^
这个模块会设置以下变量:
MyLib_FOUND - 是否找到库
MyLib_INCLUDE_DIRS - 包含目录
MyLib_LIBRARIES - 库文件
MyLib_VERSION - 版本号
#]=======================================================================]
# 查找头文件
find_path(MyLib_INCLUDE_DIR
NAMES mylib/api.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
${CMAKE_INSTALL_PREFIX}/include
PATH_SUFFIXES
mylib
mylib-1.0
)
# 查找库文件
find_library(MyLib_LIBRARY
NAMES mylib libmylib mylib-1.0
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
${CMAKE_INSTALL_PREFIX}/lib
PATH_SUFFIXES
mylib
)
# 获取版本
if(MyLib_INCLUDE_DIR AND EXISTS "${MyLib_INCLUDE_DIR}/mylib/version.h")
file(READ "${MyLib_INCLUDE_DIR}/mylib/version.h" VERSION_H)
string(REGEX MATCH "#define MYLIB_VERSION_MAJOR ([0-9]+)" _ ${VERSION_H})
set(MyLib_VERSION_MAJOR ${CMAKE_MATCH_1})
string(REGEX MATCH "#define MYLIB_VERSION_MINOR ([0-9]+)" _ ${VERSION_H})
set(MyLib_VERSION_MINOR ${CMAKE_MATCH_1})
string(REGEX MATCH "#define MYLIB_VERSION_PATCH ([0-9]+)" _ ${VERSION_H})
set(MyLib_VERSION_PATCH ${CMAKE_MATCH_1})
set(MyLib_VERSION
"${MyLib_VERSION_MAJOR}.${MyLib_VERSION_MINOR}.${MyLib_VERSION_PATCH}")
endif()
# 处理 REQUIRED 和 QUIET 参数
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib
REQUIRED_VARS MyLib_LIBRARY MyLib_INCLUDE_DIR
VERSION_VAR MyLib_VERSION
)
# 设置输出变量
if(MyLib_FOUND)
set(MyLib_LIBRARIES ${MyLib_LIBRARY})
set(MyLib_INCLUDE_DIRS ${MyLib_INCLUDE_DIR})
# 创建导入目标
if(NOT TARGET MyLib::MyLib)
add_library(MyLib::MyLib UNKNOWN IMPORTED)
set_target_properties(MyLib::MyLib PROPERTIES
IMPORTED_LOCATION "${MyLib_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MyLib_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS "MYLIB_EXPORTS"
)
# 如果有调试库,也添加
if(MyLib_LIBRARY_DEBUG)
set_property(TARGET MyLib::MyLib APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG
)
set_target_properties(MyLib::MyLib PROPERTIES
IMPORTED_LOCATION_DEBUG "${MyLib_LIBRARY_DEBUG}"
)
endif()
if(MyLib_LIBRARY_RELEASE)
set_property(TARGET MyLib::MyLib APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE
)
set_target_properties(MyLib::MyLib PROPERTIES
IMPORTED_LOCATION_RELEASE "${MyLib_LIBRARY_RELEASE}"
)
endif()
endif()
endif()
# 标记为高级变量
mark_as_advanced(MyLib_INCLUDE_DIR MyLib_LIBRARY)6.2 使用查找模块
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyApp)
# 设置模块路径
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# 查找库
find_package(MyLib REQUIRED)
find_package(MyLib COMPONENTS Extra OPTIONAL_COMPONENTS Tools)
# 使用找到的库
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE MyLib::MyLib)
target_include_directories(myapp PRIVATE ${MyLib_INCLUDE_DIRS})7. 如何使用 .cmake 文件
7.1 设置模块路径
# 添加自定义模块路径
list(APPEND CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules
${CMAKE_CURRENT_SOURCE_DIR}/cmake/find
)
# 或者使用 set
set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/cmake
${CMAKE_MODULE_PATH}
)7.2 包含模块
# 包含模块文件(不创建新作用域)
include(Utils)
include(CompilerOptions)
# 使用 include_guard() 防止重复包含
include_guard(GLOBAL) # CMake 3.10+
# 包含并创建新作用域
include(CheckIncludeFile)7.3 查找包
# 使用 find_package
find_package(MyLib REQUIRED)
find_package(OpenCV COMPONENTS core imgproc REQUIRED)
# 查找并使用自定义模块
find_package(CustomLib QUIET)
if(CustomLib_FOUND)
message("找到 CustomLib 版本: ${CustomLib_VERSION}")
endif()8. 编写自己的 .cmake 文件
8.1 模块文件模板
# MyModule.cmake - 我的自定义模块
# 防止重复包含
include_guard(GLOBAL)
# 文档注释
#[=======================================================================
MyModule
--------
这个模块提供了一些有用的函数和变量。
提供的函数:
my_function() - 执行某些操作
my_other_function() - 执行其他操作
提供的变量:
MY_MODULE_VERSION - 模块版本
#]=======================================================================]
# 模块版本
set(MY_MODULE_VERSION 1.0.0)
# 函数定义
function(my_function ARG)
message(STATUS "my_function 被调用,参数: ${ARG}")
# 函数实现...
endfunction()
function(my_other_function)
message(STATUS "my_other_function 被调用")
# 函数实现...
endfunction()
# 宏定义
macro(my_macro ARG)
message("my_macro 被调用,参数: ${ARG}")
endmacro()
# 初始化代码(只在首次包含时执行)
message(DEBUG "MyModule 已加载,版本 ${MY_MODULE_VERSION}")8.2 查找模块模板
# FindSomething.cmake
#[=======================================================================
FindSomething
-------------
查找 Something 库。
导入的目标
^^^^^^^^^^
Something::Something - Something 库目标
结果变量
^^^^^^^^^^
Something_FOUND - 是否找到
Something_INCLUDE_DIRS - 包含目录
Something_LIBRARIES - 库文件
Something_VERSION - 版本号
Something_DEFINITIONS - 编译定义
#]=======================================================================]
# 查找头文件
find_path(Something_INCLUDE_DIR
NAMES something/api.h
HINTS ENV SOMETHING_ROOT
PATHS
/usr/include
/usr/local/include
${CMAKE_INSTALL_PREFIX}/include
)
# 查找库文件
find_library(Something_LIBRARY
NAMES something libsomething
HINTS ENV SOMETHING_ROOT
PATHS
/usr/lib
/usr/local/lib
${CMAKE_INSTALL_PREFIX}/lib
)
# 获取版本
if(Something_INCLUDE_DIR AND EXISTS "${Something_INCLUDE_DIR}/something/version.h")
file(READ "${Something_INCLUDE_DIR}/something/version.h" VERSION_H)
string(REGEX MATCH "#define SOMETHING_VERSION \"([0-9.]+)\"" _ ${VERSION_H})
set(Something_VERSION ${CMAKE_MATCH_1})
endif()
# 处理 REQUIRED 和 QUIET
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Something
REQUIRED_VARS Something_LIBRARY Something_INCLUDE_DIR
VERSION_VAR Something_VERSION
)
# 设置输出变量
if(Something_FOUND)
set(Something_LIBRARIES ${Something_LIBRARY})
set(Something_INCLUDE_DIRS ${Something_INCLUDE_DIR})
set(Something_DEFINITIONS -DSOMETHING_EXPORTS)
if(NOT TARGET Something::Something)
add_library(Something::Something UNKNOWN IMPORTED)
set_target_properties(Something::Something PROPERTIES
IMPORTED_LOCATION "${Something_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${Something_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS "${Something_DEFINITIONS}"
)
endif()
endif()
mark_as_advanced(Something_INCLUDE_DIR Something_LIBRARY)9. 完整示例
9.1 项目结构
MyProject/
├── CMakeLists.txt
├── cmake/
│ ├── Utils.cmake
│ ├── CompilerOptions.cmake
│ ├── FindMyLib.cmake
│ ├── MyLibConfig.cmake.in
│ └── scripts/
│ ├── generate_version.cmake
│ └── run_tests.cmake
├── src/
│ └── ...
└── tests/
└── ...9.2 cmake/Utils.cmake
# cmake/Utils.cmake
include_guard(GLOBAL)
function(add_platform_sources TARGET)
if(WIN32)
target_sources(${TARGET} PRIVATE ${ARGN}_win.cpp)
elseif(APPLE)
target_sources(${TARGET} PRIVATE ${ARGN}_mac.cpp)
elseif(UNIX)
target_sources(${TARGET} PRIVATE ${ARGN}_linux.cpp)
endif()
endfunction()
function(add_my_executable NAME)
add_executable(${NAME} ${ARGN})
set_compiler_options(${NAME})
# 设置输出目录
set_target_properties(${NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
# 添加平台特定源文件
add_platform_sources(${NAME} platform)
endfunction()
function(print_config_summary)
message("
========================================
配置总结
========================================
系统: ${CMAKE_SYSTEM_NAME}
处理器: ${CMAKE_SYSTEM_PROCESSOR}
编译器: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}
构建类型: ${CMAKE_BUILD_TYPE}
C++ 标准: ${CMAKE_CXX_STANDARD}
安装目录: ${CMAKE_INSTALL_PREFIX}
构建目录: ${CMAKE_BINARY_DIR}
源码目录: ${CMAKE_SOURCE_DIR}
========================================
")
endfunction()9.3 cmake/CompilerOptions.cmake
# cmake/CompilerOptions.cmake
include_guard(GLOBAL)
function(set_compiler_options TARGET)
if(MSVC)
target_compile_options(${TARGET} PRIVATE
/W4
/WX
/EHsc
/MP
$<$<CONFIG:Debug>:/Zi /Od /RTC1>
$<$<CONFIG:Release>:/O2 /GL>
)
target_link_options(${TARGET} PRIVATE
$<$<CONFIG:Release>:/LTCG>
$<$<CONFIG:Debug>:/DEBUG>
)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${TARGET} PRIVATE
-Wall
-Wextra
-Wpedantic
-Werror
$<$<CONFIG:Debug>:-g -O0 -fsanitize=address>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
target_link_options(${TARGET} PRIVATE
$<$<CONFIG:Debug>:-fsanitize=address>
)
endif()
endfunction()
function(enable_warnings_as_errors TARGET)
if(MSVC)
target_compile_options(${TARGET} PRIVATE /WX)
else()
target_compile_options(${TARGET} PRIVATE -Werror)
endif()
endfunction()9.4 主 CMakeLists.txt
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject VERSION 1.0.0)
# 设置模块路径
set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/cmake
${CMAKE_MODULE_PATH}
)
# 包含自定义模块
include(Utils)
include(CompilerOptions)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 选项
option(BUILD_TESTS "构建测试" ON)
option(USE_SANITIZERS "使用地址消毒器" OFF)
# 查找库
find_package(MyLib QUIET)
if(NOT MyLib_FOUND)
message(STATUS "未找到 MyLib,使用本地版本")
add_subdirectory(third_party/mylib)
endif()
# 添加子目录
add_subdirectory(src)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# 打印配置总结
print_config_summary()10. 最佳实践
10.1 文件组织
cmake/
├── modules/ # 通用模块
│ ├── Utils.cmake
│ ├── CompilerOptions.cmake
│ └── PlatformDetect.cmake
├── find/ # 查找模块
│ ├── FindMyLib.cmake
│ └── FindCustomLib.cmake
├── toolchains/ # 工具链文件
│ ├── arm-linux.cmake
│ └── mingw-w64.cmake
├── scripts/ # 脚本文件
│ ├── generate.cmake
│ └── deploy.cmake
└── config/ # 配置文件模板
├── Config.cmake.in
└── Version.cmake.in10.2 命名规范
# 文件名:使用 PascalCase 或 kebab-case
# FindBoost.cmake
# utils.cmake
# compiler-options.cmake
# 函数名:小写加下划线
function(add_my_library)
endfunction()
# 变量名:大写加下划线
set(MY_LIBRARY_VERSION "1.0.0")
# 缓存变量:有意义的名称和帮助文本
set(MY_OPTION OFF CACHE BOOL "启用我的选项")
# 目标命名:命名空间风格
add_library(MyLib::Core ALIAS mylib_core)10.3 文档注释
#[=======================================================================
FindSomething
-------------
查找 Something 库的详细描述。
使用示例:
find_package(Something REQUIRED)
target_link_libraries(myapp PRIVATE Something::Something)
支持的组件:
Core - 核心组件(默认)
Extra - 额外功能组件
结果变量:
Something_FOUND - 是否找到库
Something_VERSION - 库版本
Something_LIBRARIES - 库文件列表
Something_INCLUDE_DIRS - 头文件目录
导入的目标:
Something::Something - 主库目标
#]=======================================================================]10.4 版本控制
# 在模块文件中定义版本
set(MY_MODULE_VERSION 1.2.3)
# 检查 CMake 版本
if(CMAKE_VERSION VERSION_LESS "3.15")
message(WARNING "推荐使用 CMake 3.15+,当前版本: ${CMAKE_VERSION}")
endif()
# 兼容性处理
if(CMAKE_VERSION VERSION_LESS "3.12")
# 使用旧版命令
else()
# 使用新特性
endif()10.5 错误处理
# 检查必需参数
function(my_function)
if(NOT ARGN)
message(FATAL_ERROR "my_function: 需要至少一个参数")
endif()
endfunction()
# 检查目标是否存在
if(NOT TARGET MyLib::MyLib)
message(FATAL_ERROR "MyLib::MyLib 目标不存在")
endif()
# 优雅降级
find_package(OptionalLib QUIET)
if(NOT OptionalLib_FOUND)
message(STATUS "OptionalLib 未找到,禁用相关功能")
set(HAVE_OPTIONAL_LIB OFF)
endif()10.6 性能考虑
# 使用 include_guard 防止重复解析
include_guard(GLOBAL)
# 缓存计算结果
set(MY_CACHED_RESULT "value" CACHE INTERNAL "缓存的结果")
# 避免在循环中执行昂贵的操作
# ❌ 不好的做法
foreach(FILE ${MANY_FILES})
execute_process(COMMAND expensive_tool ${FILE})
endforeach()
# ✅ 好的做法
# 在循环外准备数据,或使用 add_custom_command总结
.cmake 文件是 CMake 生态系统中重要的组成部分,它们用于:
- 模块化:将通用代码组织成可重用的模块
- 配置:为库提供配置信息
- 查找:定位系统中的库和依赖
- 自动化:编写独立的脚本执行任务
- 交叉编译:定义工具链文件
- 分发:为已安装的包提供配置文件
编写高质量的 .cmake 文件需要:
- 清晰的命名和组织
- 完善的文档注释
- 适当的错误处理
- 考虑性能和兼容性
- 遵循 CMake 最佳实践