Wiki
Template Extension and Common Issues
Existing English translations were reused. 34 current source blocks remain in Simplified Chinese, without triggering paid translation.
This section summarizes the most common extension methods and troubleshooting when using this CMake template.
1.Add a new source file
Current database directory in use:
file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.c"
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)
Therefore, as long as the new .cpp is placed under the src/ directory of the corresponding module, CMake will automatically collect it.
For example:
src/lib1/src/math_utils.cpp
Then rebuild:
cmake --build --preset linux-debug
If you find that the new file is not included in the compilation, you can manually re-configure:
cmake --preset linux-debug
cmake --build --preset linux-debug
2.Add new header file
Recommended path:
src/lib1/inc/lib1/math_utils.hpp
The code includes:
#include "lib1/math_utils.hpp"
Do not directly put it as:
src/lib1/inc/math_utils.hpp
Because multiple libraries may have header files with the same name. Using the inc/lib1/ structure can avoid conflicts.
3.Add a new library module
Suppose we want to add lib3.
Directory structure:
src/lib3/
├── CMakeLists.txt
├── inc/
│ └── lib3/
│ └── example.hpp
└── src/
└── example.cpp
src/lib3/CMakeLists.txt:
set(PREFIX "lib3")
file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.c"
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)
add_library(${PREFIX}_src_lib
${${PREFIX}_SRC_LIST}
)
target_include_directories(${PREFIX}_src_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/inc>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_link_libraries(${PREFIX}_src_lib
PUBLIC
project_options
PRIVATE
project_warnings
)
# ========================
# Third-party dependencies
# ========================
# =======
# Install
# =======
install(TARGETS ${PREFIX}_src_lib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/inc/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
example.hpp 使用普通的 C++ 函数声明:
#pragma once
namespace lib3 {
void example();
}
对应的 example.cpp 包含这个头文件,并提供 lib3::example() 的定义。
Then in src/CMakeLists.txt add:
add_subdirectory(lib3)
and link to the main program:
target_link_libraries(${PROJECT_NAME}
PRIVATE
lib1_src_lib
lib2_src_lib
lib3_src_lib
)
4.Add new executable file
If a project has multiple programs, for example:
src/main.cpp
src/tools/calibrate_camera.cpp
Can be added in src/CMakeLists.txt:
add_executable(calibrate_camera
${CMAKE_CURRENT_SOURCE_DIR}/tools/calibrate_camera.cpp
)
target_link_libraries(calibrate_camera
PRIVATE
project_options
project_warnings
lib1_src_lib
lib2_src_lib
)
set_target_properties(calibrate_camera PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
)
install(TARGETS calibrate_camera
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
After installation:
install/linux-debug/bin/calibrate_camera
5.Change Project Name
Top level:
project(cmake_template VERSION 1.0.0 LANGUAGES C CXX)
Change to:
project(robot_app VERSION 1.0.0 LANGUAGES C CXX)
Because the main program uses:
add_executable(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
So the executable file will be from:
cmake_template
becomes:
robot_app
The run commands in the README also need to be updated accordingly:
./install/linux-debug/bin/robot_app
6.Modify C++ standard
当前模板默认使用 C++20:
set(CMAKE_CXX_STANDARD 20)
target_compile_features(project_options INTERFACE cxx_std_20)
If you want to change C++23:
set(CMAKE_CXX_STANDARD 23)
target_compile_features(project_options INTERFACE cxx_std_23)
如果项目使用 C++17:
set(CMAKE_CXX_STANDARD 17)
target_compile_features(project_options INTERFACE cxx_std_17)
修改 cmake/ProjectOptions.cmake 中已有的这两行,并确认编译器支持对应标准模式;具体语法和标准库功能还取决于编译器及其标准库版本。本模板的 CMake 最低版本仍为 3.22。
7.Change the dynamic library to a static library
如果某个模块显式指定动态库:
add_library(${PREFIX}_src_lib SHARED
${${PREFIX}_SRC_LIST}
)
Change to a static library:
add_library(${PREFIX}_src_lib STATIC
${${PREFIX}_SRC_LIST}
)
Comparison of dynamic libraries and static libraries:
| 类型 | 优点 | 缺点 |
|---|---|---|
SHARED |
可执行文件较小,库可独立更新 | Linux 运行时要能找到相应 .so |
STATIC |
部署简单,很多代码打进可执行文件 | 可执行文件更大,更新库要重新链接 |
本模板采用下一节的配置方式:Linux 预设使用动态库,Windows 预设使用静态库。两种方式都保留同样的头文件声明和调用代码。
8.Use BUILD_SHARED_LIBS to control the library type
当前模板不写死库类型:
add_library(${PREFIX}_src_lib
${${PREFIX}_SRC_LIST}
)
Then control it in the preset:
"BUILD_SHARED_LIBS": "ON"
Or:
"BUILD_SHARED_LIBS": "OFF"
ON 选择动态库,OFF 选择静态库。当前 Linux 预设为 ON,Windows 预设为 OFF,Release 继承对应设置。不要在模块中再写死 SHARED,否则会绕开这个开关。
9.Add macro definition
Add a macro to a certain target:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_ENABLE_LOG
)
Translated to en: C++ used in:
#ifdef LIB1_ENABLE_LOG
// log code
#endif
Valued macro:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_VERSION="1.0.0"
)
Visibility selection:
| Keyword | Scenario |
|---|---|
PRIVATE |
Affects only this library's source code |
PUBLIC |
Both this library's source code and users need this macro |
INTERFACE |
This target itself does not use it; it is only passed to users |
10.Add compile option
Add options for a specific library individually:
target_compile_options(${PREFIX}_src_lib
PRIVATE
-Wshadow
)
Not recommended to use global:
add_compile_options(-Wshadow)
Because global options affect all targets, which is not conducive to troubleshooting.
11.Add include path
Recommended:
target_include_directories(${PREFIX}_src_lib
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/some_private_include
)
Not recommended:
include_directories(some_private_include)
Reason:
include_directoriesis a directory-level impact, with a larger scope.target_include_directoriescan clearly specify which target needs this path.
12.Add test directory
If tests are to be added later, the following can be introduced:
test/
└── CMakeLists.txt
Add at the top level:
include(CTest)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
In preset, the following can be controlled:
"BUILD_TESTING": "ON"
However, the current template requires not adding new examples; whether to add a test directory depends on project requirements.
13.模板已有的运行测试
原始模板已经通过 include(CTest) 启用测试。构建后可以执行 ctest --preset linux-debug 或 ctest --preset windows-debug。
eigen_example 检查原始 Eigen 输出。把 main.cpp 换成 Hello World 或其他练习以后,这个测试的输出断言也要调整;测试失败不等于新程序无法编译。添加真实业务后,应为新行为编写对应测试。
14.Clean the build and installation directories
Since the template already has .gitignore Ignore:
build/
install/
log/
So these directories are all build artifacts.
Clean up Debug:
rm -rf build/linux-debug install/linux-debug
Cleanup Release:
rm -rf build/linux-release install/linux-release
Rebuild:
cmake --preset linux-debug
cmake --build --preset linux-debug
cmake --install build/linux-debug
Windows 的 PowerShell 中,先确认对应目录存在,再清理:
Remove-Item -Recurse -Force build/windows-debug
Remove-Item -Recurse -Force install/windows-debug
cmake --preset windows-debug
清理前检查当前工作目录和要删除的路径。不同编译器、不同平台使用各自的 preset 目录,不共用同一份 CMake 缓存。
15.Why might the installation directory contain lib64
This template uses:
include(GNUInstallDirs)
Used during library installation:
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
On certain Linux distributions:
CMAKE_INSTALL_LIBDIR = lib64
So the installation result is:
install/linux-debug/lib64/
This is not an error, but rather a system convention.
Do not write by hand:
LIBRARY DESTINATION lib
Otherwise, it will bypass CMake's determination of system directories.
16.Linux 运行时报找不到 .so
The error is similar to:
error while loading shared libraries: liblib1_src_lib.so: cannot open shared object file
Common causes:
- 没有执行
cmake --install ...。 - 可执行文件不是从安装目录运行的。
- 主程序或其依赖库的
INSTALL_RPATH没设置对。 - 手动移动了
bin/或lib64/的相对位置。
本模板在 src/CMakeLists.txt 中为主程序设置:
set_target_properties(${PROJECT_NAME} PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
)
还在 src/lib1/CMakeLists.txt 中为 lib1 设置:
set_target_properties(${PREFIX}_src_lib PROPERTIES
INSTALL_RPATH "$ORIGIN"
)
前者从主程序的 bin/ 目录找到库目录,后者让 lib1 找到同目录的 lib2。如果报错提到 liblib2_src_lib.so,即使主程序已经设置了搜索路径,也应检查 lib1 的配置。修改后重新 Configure、Build 和 Install,再运行安装后的程序。
以库目录为 lib64 的系统为例,保持这种结构即可:
install/linux-debug/
├── bin/
│ └── cmake_template
└── lib64/
├── liblib1_src_lib.so
└── liblib2_src_lib.so
17.Windows 程序启动失败
先确认运行的是 build/windows-debug/bin/cmake_template.exe,并且所用 GCC 的 bin/ 在 PATH 中。默认 MSYS2 安装位置对应 C:/msys64/ucrt64/bin。
验证安装结果时,重新执行:
cmake --install build/windows-debug
./install/windows-debug/bin/cmake_template.exe
安装后保持整个目录结构再移动、运行。新增第三方依赖时,按相应库的安装要求检查运行环境。
18.CMake cannot find Eigen or OpenCV
First confirm the installation of the development package.
Eigen:
sudo apt install libeigen3-dev
sudo dnf install eigen3-devel
OpenCV:
sudo apt install libopencv-dev
sudo dnf install opencv-devel
Then reconfigure:
cmake --preset linux-debug
If the library is installed in a non-standard path, add:
cmake --preset linux-debug -DCMAKE_PREFIX_PATH=/opt/my_library
Or write to preset:
"CMAKE_PREFIX_PATH": "/opt/my_library"
19.ccache causes configure to fail
In some environments, gcc and g++ may point to a ccache wrapper. If the ccache directory is not writable, the CMake compiler check may fail.
Temporary workaround:
rm -rf build/linux-debug
CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake --preset linux-debug
This is only handled in verification or special environments, and it is not recommended to hard-code it into the template.
If you really want to specify the compiler in the preset, you can add:
"cacheVariables": {
"CMAKE_C_COMPILER": "/usr/bin/gcc",
"CMAKE_CXX_COMPILER": "/usr/bin/g++"
}
However, this reduces template generality, so it is not written by default.
20.clangd cannot find project header files or third-party libraries
If the project compiles normally, but VSCode still reports that it cannot find the project header files, Eigen, or OpenCV, it is usually not that target_include_directories was written incorrectly, but rather that clangd has not read the compilation database generated by CMake.
First, confirm that the Debug compilation database exists:
cmake --preset linux-debug
test -f build/linux-debug/compile_commands.json && echo "找到了编译数据库"
Then create in the project root directory .clangd:
CompileFlags:
CompilationDatabase: build/linux-debug
Run in the VSCode Command Palette:
clangd: Restart language server
Then go to 查看 -> 输出 -> clangd to check the logs. Normal logs should contain:
Loaded compilation database from .../build/linux-debug/compile_commands.json
If the log shows:
Failed to find compilation database
command clangd fallback
This indicates that clangd is still using fallback parameters, and the project's include paths, third-party library paths, and C++ standards may all be recognized incorrectly.
关于 compile_commands.json、.clangd、Debug/Release 数据库切换和
命令行验证的完整说明,见
CMakePresets与构建安装。
Windows 下先确认 .clangd 的 CompilationDatabase 指向 build/windows-debug,并使用正确的 clangd.exe。如果能编译却找不到标准库头文件,可按 Windows 环境搭建 配置 --query-driver。
21.VSCode does not recognize the preset
Check:
- Whether the CMake Tools extension is installed.
- Whether the opened directory is the project root directory.
CMakePresets.jsonWhether it is in the project root directory.- Whether the JSON is valid.
Command-line check:
cmake --list-presets
If the command line shows:
linux-debug
linux-release
This indicates that the preset file itself is fine.
Windows 的配置列表对应 windows-debug、windows-release。如果 CMake Tools 仍拿到旧工具路径,重启 VSCode,再用 where.exe g++、where.exe cmake 检查;更换编译器后清理对应构建缓存。
22.VSCode CMake Tools cannot run or debug
Several things need to work simultaneously for CMake Tools to run or debug:
- CMake Tools can recognize presets.
- The project has already been Configured and Built.
- CMake Tools has already selected an executable target.
- To Debug, GDB must be able to start normally.
22.1.Check preset
先确认 VSCode 打开的是项目根目录,并且能识别 CMakePresets.json。
The command line can be checked like this:
cmake --list-presets
If it can be seen:
linux-debug
linux-release
This indicates that the preset file itself is fine.
In VSCode, you need to select Configure Preset, for example:
Linux Debug
Then execute Configure.
22.2.Check target
CMake Tools needs to know which executable target you want to run or debug.
The executable target in the template is:
cmake_template
如果运行或调试按钮不可用,先确认 CMake Tools 已经选择了 cmake_template。
22.3.Check whether it has already been built
CMake Tools typically runs or debugs the executable files in the build directory, for example:
build/linux-debug/src/cmake_template
If there is no Build yet, this file does not exist, and running or debugging cannot start.
First execute:
cmake --preset linux-debug
cmake --build --preset linux-debug
Or execute Configure and Build in the VSCode CMake Tools.
22.4.Check gdb
If you are only running an ordinary program, GDB is not necessarily required. If you need to debug, you need to confirm that GDB is installed.
Command-line check:
gdb --version
If not installed, run:
sudo apt install gdb
Or:
sudo dnf install gdb
22.5.Running and debugging do not require installing every time
During daily development, CMake Tools usually runs the build directory artifacts:
./build/linux-debug/src/cmake_template
Not an installation directory artifact:
./install/linux-debug/bin/cmake_template
install Mainly used to verify whether the installation layout, header file installation, dynamic library RPATH, etc. are correct.
23..gitignore How to Handle VSCode Configuration
This template does not rely on an additional VSCode debugging configuration file, because CMake Tools can directly run or debug the current CMake target.
If you do not want to commit your personal VSCode configuration to the repository, you can simply ignore .vscode/:
.vscode/
It is also possible to ignore only common local configurations:
.vscode/settings.json
.vscode/tasks.json
If the team really wants to share certain VSCode configurations, such as recommended extensions or formatting settings, you can separately discuss which files should go into Git. Running and debugging themselves can be left to CMake Tools.
24.Core rules of the template
Finally, let's summarize the most important rules of this template:
- Top level
CMakeLists.txtonly performs overall control. - Common compilation rules are placed in
cmake/ProjectOptions.cmake. - The main program is managed by
src/CMakeLists.txt. - Each library directory manages its own source code, header files, installation, and third-party dependencies.
- Header file paths include the module name, for example
lib1/eigen3_test.hpp. - Third-party libraries are placed in the user's own
Third-party dependenciesblock. - Build parameters are placed in
CMakePresets.json; do not hard-code them in the top-level CMake. - Generated directories
build/、install/、log/are not included in Git. - In VSCode, running and debugging are handled by CMake Tools, while command-line builds are still handled by CMakePresets.
25.Windows 的运行和调试路径
前面图形界面操作中选择 cmake_template target 的方法不变。Windows 使用 windows-debug,运行的程序是 build/windows-debug/bin/cmake_template.exe。
GDB 来自同一套工具链,默认 MSYS2 路径为 C:/msys64/ucrt64/bin/gdb.exe。先用 CMake Tools 的 Debug 按钮检查;想使用 F5 时,把 CMake: Debug 绑定到 F5 即可。
如果旧版本教程中看到 cmake --fresh,注意该选项从 CMake 3.24 才加入。本模板兼容 CMake 3.22,因此这里统一用清理构建目录后重新 Configure 的方式处理缓存。参见 CMake 3.24 发布说明。