cmake 命令详解与参数实测(Ubuntu 22.04)

cmake 命令逐参数实测:三行最小工程与就地配置、-S/-B 源码构建分离、-G 生成器与 CMAKE_BUILD_TYPE 注入标志、add_subdirectory 多目录、-D 缓存变量与 option、add_library 三种库、install 与 CMAKE_INSTALL_PREFIX/DESTDIR 双前缀、CXX_STANDARD 三种声明法、find_package(Threads)、add_custom_target/configure_file、ctest 与 WILL_FAIL、生成器表达式按 CONFIG 注入、--trace/--debug-output 排错与 -P 脚本模式,全部回显在 Ubuntu 22.04(cmake 3.22.1)实测通过。

Linux命令cmakeLinux系统UbuntuUbuntu 22.04命令行2026/10/17

cmake 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 cmake 的每一个常用参数——最小工程三行 CMakeLists、就地配置与 -S/-B 源码构建目录分离、-G 生成器与 -DCMAKE_BUILD_TYPE、add_subdirectory 多目录工程、-D 缓存变量与 option、add_library 三种库形态、install 与 CMAKE_INSTALL_PREFIX/DESTDIR、CXX_STANDARD 与 target_compile_options、find_package(Threads)、add_custom_target/configure_file、enable_testing 与 ctest、生成器表达式 $<CONFIG:...>、--trace/--debug-output 排错与 -P 脚本模式,全部命令与回显已在 Ubuntu 22.04(cmake 3.22.1)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64),root 身份执行
  • cmake 版本:3.22.1(cmake --version 实测:cmake version 3.22.1)
  • 模块库:/usr/share/cmake-3.22/Modules 共 500+ 个模块文件(find_package 的探测逻辑所在)
  • 全家桶命令:cmake(配置生成)、ctest(测试驱动)、cpack(打包);ccmake/cmake-gui TUI/GUI 本环境未安装
  • 默认生成器:Unix Makefiles(CMakeCache.txt 实测 CMAKE_GENERATOR:INTERNAL=Unix Makefiles)
  • 底层工具链:gcc/g++ 11.4.0、GNU Make 4.3(本专栏姊妹篇)

💡 技术拓展:cmake 不直接编译,它做两件事——Configure(解析 CMakeLists.txt、探测编译器与依赖、写 CMakeCache.txt)和 Generate(按生成器产出 Makefile 或 build.ninja),真正干活的是 make/gcc(一层与二层工具)。cmake --build 是跨生成器的统一构建入口,cmake --install 是统一安装入口,两者让脚本不绑定具体生成器。

二、命令概览

2.1 三种用法形态

cmake [<options>] -S <src> -B <build>     # 项目配置(推荐写法)
cmake --build <dir> [<options>]           # 统一构建入口
cmake --install <dir>                     # 统一安装入口
cmake -P <script.cmake>                   # 脚本模式(无工程语义)

2.2 --help 家族

$ cmake --help | head -30
Usage
  cmake [options] <path-to-source | path-to-existing-build>
  cmake [options] <path-to-source> -B <path-to-build>
Specify a source directory to (re-)generate a build system for
it in the current working directory.  Specify an existing build
directory to re-generate its build system.
Options
  -S <path-to-source>          Explicitly specify a source directory.
  -B <path-to-build>           Explicitly specify a build directory.
  -C <initial-cache>           Pre-load a script to populate the cache.
  -D <var>[:<type>]=<value>    Create or update a cmake cache entry.
  -U <globbing_expr>           Remove matching entries from CMake cache.
  -G <generator-name>          Specify a build system generator.
  ...

$ cmake --help-command-list | head -12
3d_polarization... (按字母序列出全部命令)
add_compile_definitions
add_compile_options
add_custom_command
add_custom_target
add_definitions
add_dependencies
add_executable
add_library
add_link_options
add_subdirectory
add_test

$ cmake --help-command-list | wc -l
178

$ cmake --help-command add_executable
add_executable
--------------
Add an executable to the project using the specified source files.
  add_executable(<name> [WIN32] [EXCLUDE_FROM_ALL] [source files])
...

$ cmake --help-generator-list
CodeBlocks - Extra CodeBlocks project files.
CodeLite - Extra CodeLite project files.
Eclipse CDT4 - Extra Eclipse CDT4 project files.
Green Hills MULTI
KDevelop3
MSYS Makefiles
MinGW Makefiles
Ninja
NMake Makefiles
Unix Makefiles
Unix Makefiles(省略部分 IDE 导出器)
Visual Studio ...
Xcode

$ cmake --help-variable CMAKE_CXX_STANDARD | head -14
CMAKE_CXX_STANDARD
------------------
Default value for CXX_STANDARD property of targets.
This variable is used to initialize the CXX_STANDARD property on all
targets.  See that target property for additional information.

$ cmake --help-policy CMP0000 | head -12
CMP0000
-------
The top-level source directory must contain a straight call to the
"cmake_minimum_required" or "cmake_policy" command.
...

命令/变量/模块/策略都有对应的 --help-command、--help-variable、--help-module、--help-policy 文档查询,离线手册。

三、最小工程与就地配置

$ cat hello.cpp
#include <iostream>

int main()
{
    std::cout << "hello, cmake!\n";
    return 0;
}

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(hello CXX)

add_executable(hello hello.cpp)

$ cmake .                        # 就地配置(产物混进源码目录,不推荐但能跑)
-- The CXX compiler identification is GNU 11.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cmdemo/basic

$ ls
CMakeCache.txt  CMakeFiles  CMakeLists.txt  Makefile  cmake_install.cmake  hello.cpp

$ cmake --build .                # 统一构建入口(内部调 make)
[ 50%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

$ ./hello
hello, cmake!

$ cmake --build .                # 二次构建:增量,无事可做
Consolidate compiler generated dependencies of target hello
[100%] Built target hello

$ grep -m2 -E "CMAKE_BUILD_TYPE|CMAKE_GENERATOR:" CMakeCache.txt
CMAKE_BUILD_TYPE:STRING=
CMAKE_GENERATOR:INTERNAL=Unix Makefiles

三行 CMakeLists 即可成工程:cmake_minimum_required 声明兼容下限(CMP0000 强制顶层必须有)、project 定义工程名与语言、add_executable 声明目标。CMAKE_BUILD_TYPE 默认为空(无 -O0/-O2 之外的任何优化标志注入,g++ 按 gcc 默认无优化)。

四、-S / -B 源码与构建目录分离(out-of-source)

$ tree . 2>/dev/null || find .
./CMakeLists.txt
./src/main.cpp

$ cmake -S . -B build            # -S 源码目录、-B 构建目录
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cmdemo/outsrc/build

$ ls build | head -4
CMakeCache.txt  CMakeFiles  Makefile  cmake_install.cmake

$ cmake --build build
[100%] Built target app

$ ./build/app
out-of-source build

$ cmake -S . -B build            # 重复配置安全幂等
-- Configuring done
-- Generating done
...

$ grep -m1 CMAKE_HOME_DIRECTORY build/CMakeCache.txt
CMAKE_HOME_DIRECTORY:INTERNAL=/tmp/cmdemo/outsrc    # build 目录记住了源码位置

$ cmake --build build --target help 2>&1 | head -8
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... edit_cache
... rebuild_cache
... app
... src/main.o

out-of-source 的好处:gitignore 一个 build/ 目录即可保持源码树干净;同一源码可配多个独立构建树(如 build-debug、build-release)。

五、-G 生成器与 -DCMAKE_BUILD_TYPE

$ cmake -G "Unix Makefiles" -S . -B b1 -DCMAKE_BUILD_TYPE=Release
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cmdemo/gen/b1

$ grep -m1 CMAKE_BUILD_TYPE b1/CMakeCache.txt
CMAKE_BUILD_TYPE:STRING=Release

$ cmake --build b1 && ./b1/app
NDEBUG defined (release)          # Release 自动加 -DNDEBUG

$ cmake -S . -B b2 -DCMAKE_BUILD_TYPE=Debug
$ cmake --build b2 && ./b2/app
NDEBUG undefined (debug)

$ grep -m1 CMAKE_CXX_FLAGS b1/CMakeCache.txt     # Release/Debug 各自注入的编译标志
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMAKE_CXX_FLAGS_DEBUG:STRING=-g

$ cmake --build b1 --target app --verbose | tail -2
/usr/bin/c++ -O3 -DNDEBUG ... -o app main.cpp.o    # --verbose 展开实际编译命令

$ cmake -G Ninja -S . -B b3
CMake Error:
  Could not find ninja in the system.             # 本环境未装 ninja,G 值需真实可用
构建类型注入的 CXX_FLAGS(3.22 默认)典型用途
(空)无不建议
Debug-g断点调试
Release-O3 -DNDEBUG发布
RelWithDebInfo-O2 -g -DNDEBUG带符号的发布
MinSizeRel-Os -DNDEBUG体积敏感

六、add_subdirectory 多目录工程

$ find . -name CMakeLists.txt | sort
./CMakeLists.txt
./src/CMakeLists.txt
./src/math/CMakeLists.txt

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(calc CXX)

add_subdirectory(src/math)
add_subdirectory(src)

$ cat src/math/CMakeLists.txt
add_library(math STATIC mymath.cpp)
target_include_directories(math PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

$ cat src/CMakeLists.txt
add_executable(calc main.cpp)
target_link_libraries(calc PRIVATE math)

$ cmake -S . -B build 2>&1 | tail -3
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cmdemo/sub/build

$ cmake --build build 2>&1 | tail -6
[ 25%] Building CXX object src/math/CMakeFiles/math.dir/mymath.cpp.o
[ 50%] Linking CXX static library src/math/libmath.a
[ 50%] Built target math
[ 75%] Building CXX object src/CMakeFiles/calc.dir/main.cpp.o
[100%] Linking CXX executable src/calc
[100%] Built target calc

$ ./build/src/calc
twice(21)=42

$ ls build/src/math/*.a
build/src/math/libmath.a

构建树目录结构与源码树一一对应(build/src/calc、build/src/math/libmath.a);target_include_directories(math PUBLIC ...) 的 PUBLIC 意味着链接 math 的目标自动继承 include 路径,无需再写 -I。

七、缓存变量与 option:-D 的三种交互

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(vars CXX)

set(NORMAL "plain value")                          # 普通变量:仅本次配置有效
set(CACHED "cache value" CACHE STRING "a cached string")
option(BUILD_DOCS "build documentation" OFF)       # 布尔开关,默认 OFF
option(USE_SSL "enable ssl" ON)

if(BUILD_DOCS)
    message(STATUS "docs enabled")
else()
    message(STATUS "docs disabled")
endif()
message(STATUS "NORMAL=${NORMAL}")
message(STATUS "CACHED=${CACHED}")
message(WARNING "this is a warning demo")

$ cmake -S . -B b1 2>&1 | tail -8
-- docs disabled
-- ssl on
-- NORMAL=plain value
-- CACHED=cache value
CMake Warning at CMakeLists.txt:19 (message):
  this is a warning demo
-- Configuring done
-- Generating done

$ grep -E "^(CACHED|BUILD_DOCS|USE_SSL):" b1/CMakeCache.txt
BUILD_DOCS:BOOL=OFF
CACHED:STRING=cache value
USE_SSL:BOOL=ON

$ cmake -S . -B b2 -DCACHED=override -DBUILD_DOCS=ON 2>&1 | grep -E "docs|CACHED"
-- docs enabled
-- CACHED=override

$ grep -E "^(CACHED|BUILD_DOCS):" b2/CMakeCache.txt
BUILD_DOCS:BOOL=ON
CACHED:STRING=override

$ cmake -LA b1 2>/dev/null | tail -6      # 列出缓存变量
CMAKE_STRIP:FILEPATH=/usr/bin/strip
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
USE_SSL:BOOL=ON

要点:-D 写入缓存后持久生效(重新配置不用再传);普通 set 不进缓存;option 是带文档字符串的布尔缓存变量;message(WARNING) 有醒目排版但不中断配置。

八、add_library:STATIC / SHARED / INTERFACE

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(libs CXX)

add_library(mylib_static STATIC mylib.cpp)     # 静态库
add_library(mylib_shared SHARED mylib.cpp)     # 动态库
add_library(myheader INTERFACE)                # 纯头文件库
target_include_directories(myheader INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

set_target_properties(mylib_shared PROPERTIES OUTPUT_NAME mylib_dyn)
set_target_properties(mylib_static PROPERTIES OUTPUT_NAME mylib_sta)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE mylib_static myheader)

add_executable(app_so main.cpp)
target_link_libraries(app_so PRIVATE mylib_shared myheader)
set_target_properties(app_so PROPERTIES BUILD_RPATH ${CMAKE_BINARY_DIR})

$ cmake --build build 2>&1 | tail -8
[ 33%] Linking CXX static library libmylib_sta.a
...
[ 50%] Linking CXX shared library libmylib_dyn.so
...

$ ./build/app
answer=42 tag=v1

$ ls -l build/libmylib_sta.a build/libmylib_dyn.so
-rwxr-xr-x 1 root root 15152 Oct 17 03:00 build/libmylib_dyn.so
-rw-r--r-- 1 root root  1396 Oct 17 03:00 build/libmylib_sta.a

$ ldd build/app_so | grep mylib
        libmylib_dyn.so => /tmp/cmdemo/libs/build/libmylib_dyn.so (0x000079d728362000)

$ ./build/app_so
answer=42 tag=v1
形态产物特点
STATIC.a链接时打进可执行文件
SHARED.so运行期加载,需要 rpath/LD_LIBRARY_PATH
INTERFACE无产物只传递用法(头文件-only 库标准姿势)

BUILD_RPATH 属性直接把构建目录烧进 RUNPATH,演示用免拷贝。

九、install 与 CMAKE_INSTALL_PREFIX / DESTDIR

$ cat CMakeLists.txt(节选)
add_library(mylib STATIC mylib.cpp)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE mylib)

install(TARGETS app mylib
        RUNTIME DESTINATION bin
        ARCHIVE DESTINATION lib)
install(FILES mylib.hpp DESTINATION include/mylib)

$ cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/tmp/cmdemo/inst/prefix
$ cmake --build build 2>&1 | tail -3
$ cmake --install build
-- Install configuration: ""
-- Installing: /tmp/cmdemo/inst/prefix/bin/app
-- Installing: /tmp/cmdemo/inst/prefix/lib/libmylib.a
-- Installing: /tmp/cmdemo/inst/prefix/include/mylib/mylib.hpp

$ find prefix -type f | sort
prefix/bin/app
prefix/include/mylib/mylib.hpp
prefix/lib/libmylib.a

$ DESTDIR=/tmp/cmdemo/inst/stage cmake --install build
-- Installing: /tmp/cmdemo/inst/stage/tmp/cmdemo/inst/prefix/bin/app

$ ./build/app
42

两层前缀的分工:CMAKE_INSTALL_PREFIX 是逻辑安装前缀(configure 期决定),DESTDIR 是打包期根转移(install 命令行临时指定,路径实际拼接为 $DESTDIR + $CMAKE_INSTALL_PREFIX),做 .deb/.rpm 打包时的标准手法。

十、编译选项:CXX_STANDARD 与 target_compile_options

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(copt CXX)

add_executable(app17 main.cpp)
set_target_properties(app17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
target_compile_options(app17 PRIVATE -Wall -Wextra -O2)
target_compile_definitions(app17 PRIVATE APP_VER="1.2.3")

add_executable(app98 main.cpp)
set_target_properties(app98 PROPERTIES CXX_STANDARD 98)

add_executable(appfeat main.cpp)
target_compile_features(appfeat PRIVATE cxx_std_17)

$ cmake --build build 2>&1 | tail -6     # app98 编译结构化绑定代码失败——标准真的生效了
main.cpp:8:9: error: expected '[' before '&' token
gmake[2]: *** [CMakeFiles/app98.dir/build.make:76: ...] Error 1

$ ./build/app17
k=1

$ cmake --build build --target appfeat   # 单独构建第三个目标
[100%] Built target appfeat
$ ./build/appfeat
k=1

$ grep -m1 "CXX_FLAGS" build/CMakeFiles/app17.dir/flags.make
CXX_FLAGS = -Wall -Wextra -O2 -std=c++17

$ grep -m1 "CXX_DEFINES" build/CMakeFiles/app17.dir/flags.make
CXX_DEFINES = -DAPP_VER=\"1.2.3\"

三种声明标准的方式殊途同归:CXX_STANDARD 属性、CMAKE_CXX_STANDARD 变量(给全部目标兜底)、target_compile_features(... cxx_std_17)(按需特性)。CXX_STANDARD_REQUIRED ON 禁止降级、CXX_EXTENSIONS OFF 去掉 gnu++17 的 gnu 部分。flags.make 可核对最终命令行。

十一、find_package 与 find_library

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(findemo CXX)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
message(STATUS "Threads found: ${Threads_FOUND}")
message(STATUS "CMAKE_THREAD_LIBS_INIT=${CMAKE_THREAD_LIBS_INIT}")

find_library(MATH_LIBRARY m)
message(STATUS "MATH_LIBRARY=${MATH_LIBRARY}")

add_executable(app main.cpp)
target_link_libraries(app PRIVATE Threads::Threads ${MATH_LIBRARY})

$ cmake -S . -B build 2>&1 | grep -E "Threads|MATH|Configuring|Generating"
-- Found Threads: TRUE
-- Threads found: TRUE
-- MATH_LIBRARY=/usr/lib/x86_64-linux-gnu/libm.so
-- Configuring done
-- Generating done

$ cmake --build build 2>&1 | tail -2
[100%] Built target app

$ ./build/app
thread ok, sqrt(2)=1.41421

$ ldd build/app | grep -E "pthread|libm"
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000079fccab88000)

find_package 走 Modules 目录的探测脚本(FindThreads.cmake 等),优先导入带命名空间的目标(Threads::Threads)而非裸变量;find_library 轻量级找单个库。找不到 REQUIRED 包时配置直接失败。

十二、自定义命令与 configure_file

$ cat version.h.in
#define VERSION_STR "@BUILD_DATE@"

$ cat CMakeLists.txt
execute_process(COMMAND date +%Y%m%d
                OUTPUT_VARIABLE BUILD_DATE
                OUTPUT_STRIP_TRAILING_WHITESPACE)

add_custom_target(greet ALL
    COMMAND ${CMAKE_COMMAND} -E echo "custom target runs"
    COMMENT "saying hello")

configure_file(version.h.in version.h @ONLY)

add_executable(app main.cpp)
target_include_directories(app PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

add_custom_command(TARGET app POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo "post-build: copied to app.copied"
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:app> app.copied)

$ cmake -S . -B build 2>&1 | grep -E "BUILD_DATE|Generating"
-- BUILD_DATE=20261017

$ cmake --build build 2>&1 | grep -E "custom|post-build|Built"
custom target runs
[ 33%] Built target greet
post-build: copied to app.copied
[100%] Built target app

$ ./build/app
build ver: 20261017

$ cat build/version.h
#define VERSION_STR "20261017"

$ ls -l build/app.copied
-rwxr-xr-x 1 root root 16464 Oct 17 02:59 build/app.copied

$ cmake -E echo "portable -E mode"        # -E 是 cmake 自带的跨平台命令行工具箱
portable -E mode

$ cmake -E make_directory demo_dir && ls -d demo_dir
demo_dir

$ cmake -E remove_directory demo_dir && ls -d demo_dir
ls: cannot access 'demo_dir': No such file or directory

四类"代码生成"工具各司其职:execute_process(configure 期跑一次)、configure_file(模板变量替换生成源码)、add_custom_target(可命名、可进 ALL 的任务)、add_custom_command(TARGET ... POST_BUILD)(挂在目标构建后的钩子)。$<TARGET_FILE:app> 是生成器表达式,引用目标产物路径。

十三、测试:enable_testing / add_test / ctest

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(testdemo CXX)

enable_testing()

add_executable(test_add test_add.cpp)
add_test(NAME add_works COMMAND test_add)

add_executable(test_fail test_fail.cpp)          # 返回 2 的"必失败"用例
add_test(NAME failing_case COMMAND test_fail)
set_tests_properties(failing_case PROPERTIES WILL_FAIL TRUE)

$ cmake -S . -B build 2>&1 | tail -2
$ cmake --build build 2>&1 | tail -3

$ ctest --test-dir build
Internal ctest changing into directory: /tmp/cmdemo/test/build
Test project /tmp/cmdemo/test/build
    Start 1: add_works
1/2 Test #1: add_works ........................   Passed    0.00 sec
    Start 2: failing_case
2/2 Test #2: failing_case .....................   Passed    0.00 sec    # WILL_FAIL 反转判定

100% tests passed, 0 tests failed out of 2

$ ctest --test-dir build -R add_works -V         # -R 过滤、-V 展开细节
...
1: Test command: /tmp/cmdemo/test/build/test_add
1: test_add passed
1/1 Test #1: add_works ........................   Passed    0.00 sec

$ ctest --test-dir build -N                      # 只列出测试清单
Test #1: add_works
Test #2: failing_case
Total Tests: 2

测试约定:用例以进程退出码 0 为通过;WILL_FAIL TRUE 断言用例必失败(测试"测试");ctest 支持 -R 正则过滤、-V 详情、-N 清单、--output-on-failure(实战常用)。

十四、生成器表达式与版本注入

$ cat version.h.in
#define PROJECT_VER "@PROJECT_VERSION@"

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(genex VERSION 2.5.1 LANGUAGES CXX)      # 3.22 起 VERSION 必须配 LANGUAGES 关键字

configure_file(version.h.in version.h @ONLY)
message(STATUS "PROJECT_VERSION=${PROJECT_VERSION}")

add_executable(app main.cpp)
target_include_directories(app PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_compile_definitions(app PRIVATE
    $<$<CONFIG:Debug>:DBG_MODE>
    $<$<CONFIG:Release>:NDEBUG>)

$ cmake -S . -B brel -DCMAKE_BUILD_TYPE=Release 2>&1 | grep -E "PROJECT_VERSION|defs"
-- PROJECT_VERSION=2.5.1
-- defs=$<$<CONFIG:Debug>:DBG_MODE>;$<$<CONFIG:Release>:NDEBUG>

$ cmake --build brel 2>&1 | tail -1
[100%] Built target app

$ ./brel/app
release build, ver 2.5.1

$ cat brel/version.h
#define PROJECT_VER "2.5.1"

$ cmake -S . -B bdbg -DCMAKE_BUILD_TYPE=Debug 2>&1 | grep defs
-- defs=$<$<CONFIG:Debug>:DBG_MODE>;...

$ ./bdbg/app
debug build, ver 2.5.1

$ grep -m1 CXX_DEFINES bdbg/CMakeFiles/app.dir/flags.make
CXX_DEFINES = -DDBG_MODE                        # Debug 树只注入了 DBG_MODE

$<$<CONFIG:Debug>:DBG_MODE> 读作"若 CONFIG 是 Debug 则取 DBG_MODE"——同一份 CMakeLists 在不同构建树注入不同宏。project(... VERSION ...) 后 PROJECT_VERSION 变量可用,配合 configure_file 是版本号注入的标准流程。

十五、错误场景与 --trace / --debug-output

$ mkdir empty && cmake -S empty -B empty/build
CMake Error: The source directory "/tmp/cmdemo/err/empty" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.

$ cat badcmd/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(badcmd CXX)
this_command_does_not_exist()

$ cmake -S badcmd -B badcmd/build
CMake Error at CMakeLists.txt:3 (this_command_does_not_exist):
  Unknown CMake command "this_command_does_not_exist".
-- Configuring incomplete, errors occurred!

$ cat badvar/CMakeLists.txt          # if( 括号没闭合
if(NOT DEFINED SOMETHING
    message(STATUS "oops")
endif()

$ cmake -S badvar -B badvar/build 2>&1 | tail -5
CMake Error at CMakeLists.txt:3:
  Parse error.  Function missing ending ")".  End of file reached.
-- Configuring incomplete, errors occurred!

$ cmake -S trace -B trace/build --trace 2>&1 | head -4    # 逐命令跟踪执行流
Running with trace output on.
/tmp/cmdemo/err/trace/CMakeLists.txt(1):  cmake_minimum_required(VERSION 3.10 )
/tmp/cmdemo/err/trace/CMakeLists.txt(2):  project(trace CXX )
/usr/share/cmake-3.22/Modules/CMakeDetermineCXXCompiler.cmake(74):  set(...)

$ cmake -S trace -B trace/build2 --debug-output 2>&1 | head -8
Running with debug output on.
-- The CXX compiler identification is GNU 11.4.0
   Called from: [3] /usr/share/cmake-3.22/Modules/CMakeDetermineCompilerId.cmake
                [2] /usr/share/cmake-3.22/Modules/CMakeDetermineCXXCompiler.cmake
                [1] /tmp/cmdemo/err/trace/CMakeLists.txt

$ cmake --build trace/build 2>&1 | tail -1
Error: could not load cache                # 配置失败的目录不能构建
报错根因
does not appear to contain CMakeLists.txt-S 指错目录
Unknown CMake command "..."命令拼错或模块未 find_package
Parse error. Function missing ending ")"括号/引号未闭合
project with VERSION ... must use LANGUAGES3.22 语法要求,补 LANGUAGES 关键字
could not load cache配置未成功的目录不可 --build
Could not find ninja-G 的生成器程序未安装

排错三件套:--trace(每个命令一行,含模块内部)、--debug-output(带调用栈)、--trace-expand(连变量展开一起看)。

十六、--target clean / -P 脚本模式 / 杂项

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(misc CXX)
add_executable(app main.cpp)

$ cat helper.cmake
message(STATUS "script mode: CMAKE_SCRIPT_MODE_FILE=${CMAKE_SCRIPT_MODE_FILE}")
math(EXPR SUM "6 * 7")
message(STATUS "6*7=${SUM}")
string(TOUPPER "cmake script" UPPER)
message(STATUS "${UPPER}")
file(TOUCH /tmp/cmdemo/misc/touched_by_script)

$ cmake -S . -B build
-- Build files have been written to: /tmp/cmdemo/misc/build

$ cmake --build build
[100%] Built target app

$ cmake --build build --target help 2>&1 | grep -E "^\.\.\. (all|clean|app)" | head -4
... all (the default if no target is provided)
... clean
... app

$ cmake --build build --target clean       # 等价 make clean
$ ls build | grep -c app
0

$ cmake --build build                      # 再构建照常恢复
[100%] Built target app

$ cmake -P helper.cmake                    # 脚本模式:无工程语义,纯命令序列
-- script mode: CMAKE_SCRIPT_MODE_FILE=/tmp/cmdemo/misc/helper.cmake
-- 6*7=42
-- CMAKE SCRIPT

$ ls touched_by_script
touched_by_script

$ cmake --build build --target nonexist
gmake: *** No rule to make target 'nonexist'.  Stop.   # 底层 make 的报错透传

$ cmake --install build
CMake Error: Error processing file: /tmp/cmdemo/misc/build/cmake_install.cmake   # 无 install 规则

cmake -P 常用于 CI 里的版本号计算、文件搬运等胶水逻辑;CMAKE_SCRIPT_MODE_FILE 记录脚本自身路径。

十七、参数速查表

参数作用实测要点
-S <dir>源码目录配合 -B 实现外部构建
-B <dir>构建目录可同时维护多棵构建树
-D <var>=<val>写缓存变量持久生效,重新配置不必重传
-G <gen>指定生成器默认 Unix Makefiles;需真实安装
--build <dir>统一构建入口内部调 make/ninja
--build --target x构建指定目标help 列出全部目标
--build --verbose展开实际命令核对最终编译行
--install <dir>统一安装入口按 install() 规则落盘
-DCMAKE_BUILD_TYPE=...构建类型Release 注入 -O3 -DNDEBUG
-DCMAKE_INSTALL_PREFIX=...安装前缀与 DESTDIR 相乘拼接
-LA列缓存变量带 :TYPE 标注
--target clean清构建目录底层即 make clean
--trace / --debug-output排错跟踪逐命令 / 带调用栈
-P <script>脚本模式CI 胶水逻辑

关联阅读:gcc / g++(CMake 生成的编译命令的最终执行者)、make(CMake 默认生成器的构建引擎)见本专栏姊妹篇。