CMakeLists.txt 完全详解

张开发
2026/6/30 6:12:59 15 分钟阅读
CMakeLists.txt 完全详解
一、CMake 基础概念1.1 什么是 CMakeCMake 是一个跨平台的构建系统生成器它不直接构建项目而是生成特定平台的构建文件如 Makefile、Visual Studio 解决方案等。1.2 CMakeLists.txt 的作用定义项目的构建规则管理源文件和依赖关系配置编译和链接选项支持跨平台构建二、常用命令速查命令用途add_executable添加可执行文件add_library添加库文件target_link_libraries链接库target_include_directories添加头文件路径find_package查找外部包add_subdirectory添加子目录set设置变量message打印信息三、CMakeLists.txt 核心语法3.1 命令调用基本语法命令(参数1 参数2 …)command(arg1 arg2 arg3)example:add_executable(my_app main.cpp)带引号的参数支持空格command(“argument with spaces”)example:set(DESCRIPTION This is my project) message(Hello World)多行字符串多行字符串集中等价写法方式1使用多行括号参数command([[ This is a multi-line string with quotes ]])方式2使用普通字符串 转义换行不推荐command( This is a multi-line\n string with quotes)方式3使用 ${VAR} 或 set 存储多行文本set(MY_TEXT [[ This is a multi-line string with quotes ]]) command(${MY_TEXT})括号参数规则[[和]]之间的内容完全保留原样包括换行、缩进、引号中间可以写任意内容无需转义的数量必须匹配可以是 0 到任意多个# 0 个等号最常用 command([[ multi-line text ]]) # 2 个等号 command([[ can contain ]] without closing ]])实际 CMake 示例# 生成文件内容 file(WRITE output.txt [[ Hello world This line has quotes And ${THIS_IS_NOT_VARIABLE} (no expansion) ]]) # 执行命令时传递多行字符串 execute_process( COMMAND echo [[ line1 line2 ]] )注意CMake 的括号参数是原始字符串不会展开 ${变量}除非你用普通引号字符串。3.2 变量# 设置变量 set(MY_VARIABLE value) set(SOURCES main.cpp util.cpp math.cpp) # 使用变量${} 语法 message(STATUS Value: ${MY_VARIABLE}) # 列表操作 set(LIST a b c) list(APPEND LIST d e) list(REMOVE_ITEM LIST b) list(LENGTH LIST len) # 环境变量 set(ENV{PATH} /new/path:$ENV{PATH}) message(PATH: $ENV{PATH})3.3 条件判断# if 语法 if(expression) # 真分支 elseif(another_expression) # 其他分支 else() # 假分支 endif() # 常用表达式 if(VAR) # 变量非空且不为 0/NO/OFF/FALSE if(NOT VAR) # 取反 if(VAR1 AND VAR2) # 与 if(VAR1 OR VAR2) # 或 if(DEFINED VAR) # 变量已定义 if(COMMAND command_name) # 命令存在 if(TARGET target_name) # 目标存在 if(EXISTS path) # 文件或目录存在 if(IS_DIRECTORY path) # 是目录 if(CMAKE_SYSTEM_NAME STREQUAL Linux) # 字符串比较 if(VERSION GREATER 3.10) # 版本比较3.4 循环# foreach 循环 foreach(var IN ITEMS a b c d) message(Item: ${var}) endforeach() # 范围循环 foreach(i RANGE 10) # 0-10 foreach(i RANGE 1 10 2) # 1 3 5 7 9 endforeach() # 列表循环 set(FILES file1.cpp file2.cpp) foreach(file ${FILES}) message(Processing: ${file}) endforeach() # while 循环 set(counter 0) while(counter LESS 5) message(Counter: ${counter}) math(EXPR counter ${counter} 1) endwhile()四、项目组织4.1 目录结构示例project/ ├── CMakeLists.txt # 根 CMakeLists.txt ├── include/ # 公共头文件 │ └── project/ │ └── api.h ├── src/ # 源文件目录 │ ├── CMakeLists.txt │ ├── main.cpp │ └── module/ │ ├── CMakeLists.txt │ └── impl.cpp ├── tests/ # 测试目录 │ ├── CMakeLists.txt │ └── test_main.cpp ├── libs/ # 第三方库 │ └── external_lib/ └── cmake/ # CMake 模块 └── FindMyLib.cmake4.2 根 CMakeLists.txt 模板# 版本要求 cmake_minimum_required(VERSION 3.15) # 项目定义 project(MyProject VERSION 1.0.0 DESCRIPTION My awesome project LANGUAGES CXX C HOMEPAGE_URL https://example.com ) # 设置策略 cmake_policy(SET CMP0077 NEW) # 构建类型 if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING Build type FORCE) endif() # C 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # 编译选项 if(CMAKE_CXX_COMPILER_ID MATCHES GNU|Clang) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall -Wextra) set(CMAKE_CXX_FLAGS_DEBUG -g -O0) set(CMAKE_CXX_FLAGS_RELEASE -O3 -DNDEBUG) endif() # 输出目录 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 包含 CMake 模块路径 list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) # 添加子目录 add_subdirectory(src) if(BUILD_TESTING) enable_testing() add_subdirectory(tests) endif() # 安装配置 include(GNUInstallDirs) install(TARGETS my_target EXPORT MyProjectTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} )五、目标详解5.1 目标类型# 可执行文件 add_executable(app main.cpp) # 静态库 add_library(mylib STATIC lib.cpp) # 动态库/共享库 add_library(myshared SHARED lib.cpp) # 对象库不链接只编译 add_library(myobj OBJECT obj.cpp) # 接口库纯头文件库 add_library(header_only INTERFACE) target_include_directories(header_only INTERFACE include/) # 导入库已存在的库 add_library(external_lib UNKNOWN IMPORTED) set_target_properties(external_lib PROPERTIES IMPORTED_LOCATION /path/to/lib.so )5.2 目标属性# 设置属性 set_target_properties(target_name PROPERTIES CXX_STANDARD 17 OUTPUT_NAME custom_name VERSION 1.2.3 SOVERSION 1 POSITION_INDEPENDENT_CODE ON ) # 获取属性 get_target_property(prop target_name PROPERTY_NAME) # 目标包含目录 target_include_directories(target PRIVATE src # 仅目标自身使用 PUBLIC include # 目标和使用者都使用 INTERFACE api # 仅使用者使用 ) # 目标编译定义 target_compile_definitions(target PRIVATE DEBUG_MODE1 PUBLIC USE_FEATURE ) # 目标编译选项 target_compile_options(target PRIVATE -Wall -Wextra PUBLIC -pthread ) # 目标链接库 target_link_libraries(target PRIVATE my_internal_lib PUBLIC external_lib INTERFACE header_only_lib ) # 链接选项 target_link_options(target PRIVATE -static-libstdc )六、依赖管理6.1 find_package# 查找包 find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system) # 使用包 if(Boost_FOUND) target_link_libraries(myapp ${Boost_LIBRARIES}) target_include_directories(myapp PRIVATE ${Boost_INCLUDE_DIRS}) endif() # 现代 CMake 方式使用导入目标 find_package(OpenCV REQUIRED) target_link_libraries(myapp PRIVATE opencv::core opencv::imgproc)6.2 FetchContentCMake 3.11include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.11.0 ) FetchContent_MakeAvailable(googletest) target_link_libraries(my_test PRIVATE gtest_main)6.3 ExternalProjectinclude(ExternalProject) ExternalProject_Add( project_zlib URL https://zlib.net/zlib-1.2.11.tar.gz CONFIGURE_COMMAND ./configure --prefix${CMAKE_BINARY_DIR}/external BUILD_COMMAND make INSTALL_COMMAND make install )七、高级特性7.1 生成器表达式# 条件编译 target_compile_definitions(app PRIVATE $$CONFIG:Debug:DEBUG_MODE $$PLATFORM_ID:Windows:_WIN32 ) # 包含目录 target_include_directories(lib PRIVATE $BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include $INSTALL_INTERFACE:include ) # 链接库条件 target_link_libraries(app PRIVATE $$CXX_COMPILER_ID:GNU:stdcfs $$CXX_COMPILER_ID:Clang:cfs )7.2 自定义命令和目标# 添加自定义命令 add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.h COMMAND ${CMAKE_COMMAND} -E echo Generating file... COMMAND python generate.py ${CMAKE_CURRENT_SOURCE_DIR}/input.txt DEPENDS generate.py input.txt COMMENT Generating header file ) # 添加自定义目标 add_custom_target(generate_files ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h ) # 依赖关系 add_dependencies(app generate_files)7.3 配置文件生成# 创建 config.h.in configure_file( ${CMAKE_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/config.h ) # config.h.in 内容 #cmakedefine HAVE_FEATURE #cmakedefine VERSION PROJECT_VERSION7.4 导出和安装# 导出目标 export(TARGETS mylib FILE ${CMAKE_BINARY_DIR}/MyLibTargets.cmake NAMESPACE MyLib:: ) # 安装导出 install(EXPORT MyLibTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyLib NAMESPACE MyLib:: ) # 创建包配置 include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( ${CMAKE_SOURCE_DIR}/cmake/MyLibConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyLib )八、调试和诊断8.1 消息输出# 消息级别 message(STATUS Info message) # 正常信息 message(WARNING Warning) # 警告 message(AUTHOR_WARNING Dev warning) # 开发警告 message(SEND_ERROR Error) # 错误但不终止 message(FATAL_ERROR Fatal) # 致命错误终止8.2 变量调试# 打印所有变量 get_cmake_property(vars VARIABLES) foreach(var ${vars}) message(${var}${${var}}) endforeach() # 打印缓存变量 get_cmake_property(cache_vars CACHE_VARIABLES) foreach(var ${cache_vars}) message(${var}${${var}}) endforeach()九、最佳实践9.1 现代 CMake 建议使用目标导向优先使用target_*命令而非全局*_DIRECTORIES明确可见性始终指定PRIVATE/PUBLIC/INTERFACE避免全局变量尽量减少include_directories、link_directories使用生成器表达式处理条件依赖和平台差异模块化使用add_subdirectory组织代码9.2 性能优化# 避免使用 file(GLOB) # 不推荐 file(GLOB SOURCES src/*.cpp) # 推荐显式列出 set(SOURCES src/main.cpp src/feature1.cpp src/feature2.cpp ) # 使用 Ninja 生成器 # cmake -G Ninja -B build9.3 常见陷阱# 错误变量作用域 set(VAR global) function(myfunc) set(VAR local) # 局部变量 endfunction() # 正确使用 PARENT_SCOPE function(myfunc) set(VAR local PARENT_SCOPE) endfunction() # 缓存变量 set(MY_CACHE value CACHE STRING Description)十、完整实战示例# 根 CMakeLists.txt cmake_minimum_required(VERSION 3.15) project(Calculator VERSION 1.0.0) # 构建配置 set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 选项 option(BUILD_TESTS Build tests ON) option(BUILD_SHARED_LIBS Build shared libraries OFF) # 包含子目录 add_subdirectory(src) if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() # 安装 include(GNUInstallDirs) install(TARGETS calculator RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )# src/CMakeLists.txt add_library(calc_lib STATIC calculator.cpp operations.cpp ) target_include_directories(calc_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_compile_definitions(calc_lib PRIVATE $$CONFIG:Debug:DEBUG ) add_executable(calculator main.cpp) target_link_libraries(calculator PRIVATE calc_lib) # 版本信息 set_target_properties(calc_lib PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1 )十一、构建命令11.1 配置项目生成构建系统# 基础用法推荐cmake-Bbuild-S.# 指定生成器cmake-Bbuild-GNinja# 指定构建类型单配置生成器cmake-Bbuild-DCMAKE_BUILD_TYPERelease# 多配置生成器如VScmake-Bbuild-DCMAKE_CONFIGURATION_TYPESDebug;Release# 设置安装路径cmake-Bbuild-DCMAKE_INSTALL_PREFIX/usr/local# 传递自定义变量cmake-Bbuild-DMY_VARIABLEON# 查看所有配置选项cmake-Bbuild-LAH11.2 构建项目# 基础构建cmake--buildbuild# 指定构建配置cmake--buildbuild--configRelease# 并行构建指定线程数cmake--buildbuild--parallel8# 或简写cmake--buildbuild-j8# 指定目标cmake--buildbuild--targetmy_target# 清理清空构建产物cmake--buildbuild--targetclean# 显示详细编译命令cmake--buildbuild--verbose# 或cmake--buildbuild-v

更多文章