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)实测通过。
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-guiTUI/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 LANGUAGES | 3.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 默认生成器的构建引擎)见本专栏姊妹篇。