Template Extensions and FAQ
This section outlines the most common extensions and troubleshooting when using this CMake template.
Add new source files
Current repository directory use:
file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/src/*.c"
"${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)
So as long as you place the new .cpp into the corresponding module's src/ directory, CMake will automatically collect it.
For example:
src/lib1/src/math_utils.cpp
Then rebuild:
cmake --build --preset linux-debug
If you find that new files aren't being compiled, you can manually reconfigure:
cmake --preset linux-debug
cmake --build --preset linux-debug
Add a new header file
Recommended path:
src/lib1/inc/lib1/math_utils.hpp
Code contains:
#include "lib1/math_utils.hpp"
Don't just put it as:
src/lib1/inc/math_utils.hpp
Because multiple libraries might have identically named header files. Using a structure like inc/lib1/ avoids such naming conflicts.
Add a new library module
Suppose you 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 SHARED
${${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}
)
Then add to src/CMakeLists.txt:
add_subdirectory(lib3)
and link to the main program:
target_link_libraries(${PROJECT_NAME}
PRIVATE
lib1_src_lib
lib2_src_lib
lib3_src_lib
)
Add a new executable file
If a project has multiple programs, for example:
src/main.cpp
src/tools/calibrate_camera.cpp
You can add 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
Change project name
Top-level:
project(cmake_template VERSION 1.0.0 LANGUAGES C CXX)
Please provide the Simplified Chinese Markdown fragment you'd like me to translate.
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 come from:
cmake_template
Change to:
robot_app
Update the run commands in the README as well:
./install/linux-debug/bin/robot_app
Modify the C++ standard.
Current:
set(CMAKE_CXX_STANDARD 17)
target_compile_features(project_options INTERFACE cxx_std_17)
If you want to change C++20:
set(CMAKE_CXX_STANDARD 20)
target_compile_features(project_options INTERFACE cxx_std_20)
If one were to modify C++23:
set(CMAKE_CXX_STANDARD 23)
target_compile_features(project_options INTERFACE cxx_std_23)
Also confirm that the compiler supports the corresponding standard.
Change dynamic library to static library
Current:
add_library(${PREFIX}_src_lib SHARED
${${PREFIX}_SRC_LIST}
)
Change to static library:
add_library(${PREFIX}_src_lib STATIC
${${PREFIX}_SRC_LIST}
)
Comparison between dynamic libraries and static libraries:
| type | Advantages | Disadvantages |
|---|---|---|
SHARED | Executable files are smaller, and libraries can be updated independently. | Runtime should be able to find .so |
STATIC | Easy deployment, with much code compiled into executable files. | Executable files are larger, and updating libraries requires relinking. |
The default template SHARED is for demonstrating dynamic library installation and RPATH setup.
Use BUILD_SHARED_LIBS to control library types
You can also skip SHARED:
add_library(${PREFIX}_src_lib
${${PREFIX}_SRC_LIST}
)
Then control it in the preset.
"BUILD_SHARED_LIBS": "ON"
or
"BUILD_SHARED_LIBS": "OFF"
In this way, the same template can switch between dynamic and static libraries through configuration.
Add macro definition
Add macros to a specific target:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_ENABLE_LOG
)
Used in C++:
#ifdef LIB1_ENABLE_LOG
// log code
#endif
Macro with values:
target_compile_definitions(${PREFIX}_src_lib
PRIVATE
LIB1_VERSION="1.0.0"
)
Visibility selection:
| Keywords | Scene |
|---|---|
PRIVATE | Only affects this repository's source code. |
PUBLIC | This library's source code and its users both require this macro. |
INTERFACE | This target is not used internally; it is only passed to users. |
Add compilation options
Add individual options for a specific library:
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.
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_directorieshas a directory-level impact, covering a broader scope.- Clearly indicate which target requires this path.
Add a test directory
If you want to add tests later, you can add:
test/
└── CMakeLists.txt
Add at the top level:
include(CTest)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
In preset, you can control:
"BUILD_TESTING": "ON"
However, the current template does not allow adding new examples, and whether to include the test directory depends on project needs.
Clean build and installation directories
Since the template already contains .gitignore, ignore:
build/
install/
log/
So these directories are all generated artifacts.
Clear the debug output.
rm -rf build/linux-debug install/linux-debug
Clean Release:
rm -rf build/linux-release install/linux-release
Reconstruct:
cmake --preset linux-debug
cmake --build --preset linux-debug
cmake --install build/linux-debug
为什么安装目录里可能是 lib64?
This template is used for:
include(GNUInstallDirs)
When installing the library, use:
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
On some Linux distributions:
CMAKE_INSTALL_LIBDIR = lib64
So the installation result is:
install/linux-debug/lib64/
This is not an error, but a system convention.
Please do not handwrite:
LIBRARY DESTINATION lib
Otherwise, it would bypass CMake's system directory check.
Runtime error: cannot find .so
Errors similar to:
error while loading shared libraries: liblib1_src_lib.so: cannot open shared object file
Common causes:
- was not executed
cmake --install .... - The executable file is not run from the installation directory.
INSTALL_RPATHis not set up correctly.- Manually adjusted the relative positions of
bin/andlib64/.
This template settings:
set_target_properties(${PROJECT_NAME} PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
)
So just keep this structure:
install/linux-debug/
├── bin/
│ └── cmake_template
└── lib64/
├── liblib1_src_lib.so
└── liblib2_src_lib.so
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
Reconfigure again:
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"
ccache causes configure to fail
In certain environments, gcc and g++ may point to ccache wrappers. If the ccache directory is not writable, CMake might fail when checking the compiler.
Temporary fix:
CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake --fresh --preset linux-debug
This is merely for verification or handling under special conditions and is not recommended to be hardcoded into templates.
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++"
}
But this reduces template versatility, so it is not written by default.
Clangd cannot find project headers or third-party libraries.
This usually happens because Clangd doesn't have the correct compilation database (compile_commands.json) or includes paths.
Here are the common solutions:
1. Generate compile_commands.json (Most Common Fix)
This file tells Clangd exactly how your project is built.
- For CMake projects: Run CMake with the flag:
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
This will create acompile_commands.jsonfile in your build directory. - For Makefile or other build systems: Use tools like:
- Move the file: Copy the generated
compile_commands.jsonto your project's root directory, where Clangd can find it.
2. Configure the .clangd File
Create a .clangd file in your project root to explicitly add include paths.
CompileFlags:
Add:
- -I/path/to/your/project/includes
- -I/path/to/your/third-party/lib/include
3. Verify Your compile_commands.json
Open the file and check if the paths listed in the "file" and "command" fields actually exist on your machine.
4. Restart the LSP
After making changes, restart the Clangd language server in your editor (e.g., VSCode, Neovim) for it to reload the configuration.
如果工程能够正常编译,但VSCode仍然提示找不到项目头文件、Eigen库或其他依赖项,请尝试以下步骤进行排查。通常这类问题是由于VSCode的C/C++扩展未能正确读取项目编译配置所导致的。
1. 确认构建系统配置 首先,确保您的项目构建系统(例如CMake或Make)能够正确生成用于IDE的配置文件。
- 对于CMake项目:重新运行CMake配置步骤,确保生成
compile_commands.json文件。这是VSCode等工具理解项目结构和编译命令的关键。 - 对于Makefile项目:您可能需要手动配置包含路径,或通过工具生成
compile_commands.json。
2. 配置VSCode的C/C++扩展
打开命令面板(Ctrl+Shift+P),运行“C/C++: Edit Configurations (JSON)”命令。这将打开或创建.vscode/c_cpp_properties.json文件。您需要确保其中的includePath包含了所有必需的头文件路径。
- 可以使用
${workspaceFolder}/**来递归包含工作区下的所有路径。 - 对于系统或第三方库(如Eigen),您需要添加其绝对路径或相对于工作区的路径。
3. 设置编译器路径
在c_cpp_properties.json文件中,请确保compilerPath正确指向您项目实际使用的编译器(如GCC、Clang或MSVC)。VSCode的IntelliSense引擎需要它来查找系统头文件。
4. 重建IntelliSense数据库 有时,VSCode的缓存数据库可能已过时。可以尝试以下操作:
- 关闭并重新打开VSCode。
- 在命令面板中运行“C/C++: Reset IntelliSense Database”命令。
通过以上步骤,通常可以解决VSCode与项目构建系统配置不同步导致的头文件识别问题。如果问题依旧,请检查项目的CMake或构建脚本中是否定义了可能影响IDE解析的特殊宏或变量。
OpenCV, it's usually not that target_include_directories was written wrong, but rather that clangd is.
Have not read the compilation database generated by CMake.
First, confirm the existence of the Debug compile database:
cmake --preset linux-debug
test -f build/linux-debug/compile_commands.json && echo "找到了编译数据库"
Then create .clangd in the project root directory:
CompileFlags:
CompilationDatabase: build/linux-debug
In the VSCode command panel, run:
clangd: Restart language server
Then check the logs at 查看 -> 输出 -> clangd. Normal logs should include:
Loaded compilation database from .../build/linux-debug/compile_commands.json
If the log shows:
Failed to find compilation database
command clangd fallback
说明 clangd 仍在使用回退参数,项目的 include 路径、第三方库 Paths and C++ standards might both be misidentified.
About compile_commands.json, .clangd, Debug/Release database switching, and
For complete command line verification instructions, see
CMakePresets and Build/Install。
VSCode doesn't recognize the preset.
Check:
- To check if the CMake Tools extension is installed:
- Is the opened directory the project root directory?
- Is
CMakePresets.jsonin the project root directory? - Is JSON valid?
Command line check:
cmake --list-presets
If the command line can show:
linux-debug
linux-release
This indicates the preset file is fine.
VSCode CMake Tools cannot run or debug
CMake Tools requires several things to work correctly together to run or debug.
- CMake Tools supports CMake Presets, which allow you to define and share build configurations easily.
- The project has been configured and built.
- CMake Tools has already selected the executable target.
- If you need to debug, GDB can start normally.
check preset
First, confirm that VSCode opens the project root directory and can recognize CMakePresets.json.
You can check it via the command line like this:
cmake --list-presets
If you can see:
linux-debug
linux-release
This indicates the preset file is fine.
In VSCode, you need to select Configure Preset, for example:
Linux Debug
Then run Configure.
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
If the run or debug button is unavailable, first confirm that CMake Tools has selected cmake_template.
check if it has been built
CMake Tools typically runs or debugs executables from the build directory, for example:
build/linux-debug/src/cmake_template
If you haven't built yet, this file won't exist, and you won't be able to start running or debugging.
First, execute:
cmake --preset linux-debug
cmake --build --preset linux-debug
or run Configure and Build in VSCode CMake Tools.
Check gdb
If you're just running a program normally, GDB isn't necessarily required. However, if you need to debug, you should confirm that GDB is installed.
Command line check:
gdb --version
If not installed, then execute:
sudo apt install gdb
or
sudo dnf install gdb
Run and debug without needing to install every time.
During daily development, CMake Tools usually operates on the build directory artifacts:
./build/linux-debug/src/cmake_template
Not an installation directory artifact:
./install/linux-debug/bin/cmake_template
install is mainly used to verify that the installation layout, header file installation, and dynamic library RPATH are set up correctly.
.gitignore How to handle VSCode configuration
This template does not rely on additional VSCode debug configuration files because CMake Tools can directly run or debug the current CMake target.
If you don't want to commit your personal VSCode configurations to the repository, you can simply ignore .vscode/:
.vscode/
You can also just ignore 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, it can be discussed separately which files should be included in Git. Running and debugging can be delegated to CMake Tools.
Core rules of the template
Finally, here is a summary of the most important rules for this template:
- The top-level
CMakeLists.txtonly handles control functions. - Common compilation rules are stored 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.
- The header file path includes the module name, for example,
lib1/eigen3_test.hpp. - Third-party libraries are placed in the user's own
Third-party dependenciesblock. - Place build parameters in
CMakePresets.jsonand do not hardcode them in the top-level CMakeLists.txt. - Generate directory
build/,install/,log/exclude from Git. - In VSCode, running and debugging are handled by CMake Tools, while command-line builds are still managed by CMakePresets.