1. CMake快速入门从安装到第一个项目刚接触CMake时我完全被那些晦涩的语法吓到了。直到接手一个跨平台项目才被迫真正学习它。现在回想起来CMake其实就像乐高说明书——只要掌握基本规则就能组合出强大的构建系统。1.1 跨平台安装指南在Ubuntu上安装CMake简单到令人发指sudo apt update sudo apt install cmake但实际项目中我们往往需要特定版本。比如去年我遇到个旧项目要求CMake 3.15而系统默认是3.10。这时源码编译更靠谱wget https://cmake.org/files/v3.15/cmake-3.15.7.tar.gz tar -zxvf cmake-3.15.7.tar.gz cd cmake-3.15.7 ./bootstrap make -j$(nproc) sudo make installWindows用户可以直接下载安装包但记得勾选Add to PATH。有次我忘记勾选在VS Code里折腾了半天环境变量。验证安装是否成功cmake --version这个命令我每天要敲几十次特别是在切换不同项目环境时。1.2 第一个CMake项目实战让我们从最简单的Hello World开始。创建项目目录my_project/ ├── CMakeLists.txt └── main.cppmain.cpp内容#include iostream int main() { std::cout Hello CMake! std::endl; return 0; }CMakeLists.txt最小配置cmake_minimum_required(VERSION 3.10) project(MyFirstProject) add_executable(hello main.cpp)构建流程养成build目录隔离的好习惯mkdir build cd build cmake .. make ./hello我第一次运行时看到终端输出Hello CMake!时那种成就感至今难忘。这比直接写Makefile简单多了2. CMake核心命令深度解析2.1 项目定义与变量操作project()命令远比看起来强大。最近的项目中我这样使用project( LidarSLAM VERSION 1.2.3 LANGUAGES CXX CUDA # 支持混合编程 DESCRIPTION A cross-platform SLAM system )变量是CMake的灵魂。有几点特别容易踩坑变量作用域函数内部设置的变量默认局部环境变量访问$ENV{HOME}注意大小写缓存变量set(MY_VAR value CACHE STRING 说明)我常用的调试技巧message(STATUS PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}) message(STATUS Current variables: ${CMAKE_CURRENT_SOURCE_DIR})2.2 目标构建艺术add_executable和add_library是构建系统的核心。上周我重构项目时这样组织# 先声明目标 add_library(common STATIC src/utils.cpp include/utils.h) add_executable(main_app src/main.cpp) # 后设置属性 target_include_directories(common PUBLIC include) target_link_libraries(main_app PRIVATE common)这种先声明后配置的模式让CMake更清晰。特别注意PUBLIC影响自身和使用者PRIVATE仅影响自身INTERFACE仅影响使用者动态库版本控制也很实用set_target_properties(mylib PROPERTIES VERSION 1.2.3 SOVERSION 1 OUTPUT_NAME mylib )3. 实战配置技巧大全3.1 多目录项目组织真实项目往往这样组织project/ ├── CMakeLists.txt ├── include/ ├── src/ │ ├── module1/ │ └── module2/ └── tests/根CMakeLists.txt示例cmake_minimum_required(VERSION 3.12) project(MegaProject LANGUAGES CXX) # 添加子目录 add_subdirectory(src/module1) add_subdirectory(src/module2) # 可执行文件 add_executable(main_app src/main.cpp) target_link_libraries(main_app PRIVATE module1 module2 )子目录CMakeLists.txt示例# src/module1/CMakeLists.txt file(GLOB_RECURSE SOURCES *.cpp) add_library(module1 STATIC ${SOURCES}) target_include_directories(module1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. )3.2 第三方库集成实战查找系统库的正确姿势find_package(OpenCV REQUIRED COMPONENTS core imgproc) if(NOT OpenCV_FOUND) message(FATAL_ERROR OpenCV not found!) endif() target_link_libraries(my_app PRIVATE ${OpenCV_LIBS})当库不在标准路径时我这样处理set(OpenCV_DIR /opt/opencv_4.5.2/share/OpenCV) find_package(OpenCV REQUIRED)对于没有CMake支持的库手动配置find_path(MYLIB_INCLUDE_DIR mylib.h PATHS /usr/local/include) find_library(MYLIB_LIBRARY mylib PATHS /usr/local/lib) if(MYLIB_INCLUDE_DIR AND MYLIB_LIBRARY) add_library(mylib::mylib UNKNOWN IMPORTED) set_target_properties(mylib::mylib PROPERTIES IMPORTED_LOCATION ${MYLIB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${MYLIB_INCLUDE_DIR} ) endif()4. 高级技巧与性能优化4.1 条件编译与平台适配处理跨平台差异时我这样写if(WIN32) add_definitions(-DWINDOWS_PLATFORM) set(PLATFORM_LIBS ws2_32) elseif(UNIX AND NOT APPLE) add_definitions(-DLINUX_PLATFORM) set(PLATFORM_LIBS pthread) endif() target_link_libraries(my_app PRIVATE ${PLATFORM_LIBS})编译器特性检测也很重要include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-stdc17 HAS_CXX17) if(HAS_CXX17) target_compile_features(my_app PRIVATE cxx_std_17) else() message(WARNING C17 not supported!) endif()4.2 构建优化策略并行编译加速cmake --build . --parallel 8我常用的编译选项配置if(CMAKE_BUILD_TYPE STREQUAL Release) add_compile_options(-O3 -marchnative) else() add_compile_options(-g -O0 -Wall -Wextra) endif()单元测试集成使用CTestenable_testing() add_test(NAME my_test COMMAND test_executable)安装规则配置install(TARGETS my_app DESTINATION bin) install(DIRECTORY include/ DESTINATION include) install(FILES LICENSE README.md DESTINATION share/doc)记得有次我忘记设置CMAKE_INSTALL_PREFIX结果文件装到了系统目录不得不手动清理。现在我都显式指定cmake -DCMAKE_INSTALL_PREFIX../install ..