第 22.5 節

Template Extensions and FAQ

0瀏覽次數0訪問次數--跳出率--平均停留

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:

typeAdvantagesDisadvantages
SHAREDExecutable files are smaller, and libraries can be updated independently.Runtime should be able to find .so
STATICEasy 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:

KeywordsScene
PRIVATEOnly affects this repository's source code.
PUBLICThis library's source code and its users both require this macro.
INTERFACEThis 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:

  1. include_directories has a directory-level impact, covering a broader scope.
  2. 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:

  1. was not executed cmake --install ....
  2. The executable file is not run from the installation directory.
  3. INSTALL_RPATH is not set up correctly.
  4. Manually adjusted the relative positions of bin/ and lib64/.

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 a compile_commands.json file in your build directory.
  • For Makefile or other build systems: Use tools like:
    • bear: bear -- make your-target
    • compiledb: make-dry-run | compiledb
  • Move the file: Copy the generated compile_commands.json to 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:

  1. To check if the CMake Tools extension is installed:
  2. Is the opened directory the project root directory?
  3. Is CMakePresets.json in the project root directory?
  4. 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.

  1. CMake Tools supports CMake Presets, which allow you to define and share build configurations easily.
  2. The project has been configured and built.
  3. CMake Tools has already selected the executable target.
  4. 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:

  1. The top-level CMakeLists.txt only handles control functions.
  2. Common compilation rules are stored in cmake/ProjectOptions.cmake.
  3. The main program is managed by src/CMakeLists.txt.
  4. Each library directory manages its own source code, header files, installation, and third-party dependencies.
  5. The header file path includes the module name, for example, lib1/eigen3_test.hpp.
  6. Third-party libraries are placed in the user's own Third-party dependencies block.
  7. Place build parameters in CMakePresets.json and do not hardcode them in the top-level CMakeLists.txt.
  8. Generate directory build/, install/, log/ exclude from Git.
  9. In VSCode, running and debugging are handled by CMake Tools, while command-line builds are still managed by CMakePresets.
音乐页