第 22 節

CMake Project Template

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

This chapter uses a Linux-only CMake project template to explain how to organize, build, install, and integrate third-party libraries in a C++ project.

The goal of this template is not to cover every aspect of CMake syntax all at once, but rather to focus on a real, runnable project to understand the most commonly used set of practices in modern CMake.

  1. The top-level CMakeLists.txt only handles overall project control.
  2. CMakePresets.json Lock in the Debug / Release build configurations.
  3. cmake/ProjectOptions.cmake Save common project compilation options.
  4. src/CMakeLists.txt manages the main program.
  5. Each library directory manages its own source code, headers, installation rules, and third-party dependencies.
  6. Third-party libraries follow a "whoever uses them, whoever find_package" approach.

Template Directory Structure

The core directories of the template are as follows:

.
├── CMakeLists.txt
├── CMakePresets.json
├── cmake/
│   └── ProjectOptions.cmake
└── src/
    ├── CMakeLists.txt
    ├── main.cpp
    ├── lib1/
    │   ├── CMakeLists.txt
    │   ├── inc/
    │   │   └── lib1/
    │   │       └── eigen3_test.hpp
    │   └── src/
    │       └── eigen3_test.cpp
    └── lib2/
        ├── CMakeLists.txt
        ├── inc/
        │   └── lib2/
        │       └── eigen3_test.hpp
        └── src/
            └── eigen3_test.cpp

Among them:

fileResponsibilities
CMakeLists.txtTop-level entry point, declares the project, loads common configuration, and enters src.
CMakePresets.jsonSave build presets, such as linux-debug and linux-release.
cmake/ProjectOptions.cmakeSave C/C++ standards, common warnings, and other configurations.
src/CMakeLists.txtCreate the final executable file and link lib1, lib2
src/lib1/CMakeLists.txtCreate lib1_src_lib to manage lib1's own includes, dependencies, and installation.
src/lib2/CMakeLists.txtCreate lib2_src_lib to manage lib2's own includes, dependencies, and installations.

Build Command

This template uses CMakePresets + Ninja.

Debug Build:

cmake --preset linux-debug
cmake --build --preset linux-debug
cmake --install build/linux-debug
./install/linux-debug/bin/cmake_template

Release build:

cmake --preset linux-release
cmake --build --preset linux-release
cmake --install build/linux-release
./install/linux-release/bin/cmake_template

These commands do four things respectively.

commandeffect
cmake --preset linux-debugConfigure the project and generate Ninja build files.
cmake --build --preset linux-debugCompile source code and link objects
cmake --install build/linux-debugInstall the executables, libraries, and header files to install/linux-debug.
./install/linux-debug/bin/cmake_templateRun the program after installation

During routine development, installation isn't necessary every time; after compiling, you can directly run the program from the build directory.

./build/linux-debug/src/cmake_template

install is more suitable for verifying "whether the post-installation directory structure and dynamic library paths are correct."

Configure, build, and install are three distinct stages commonly used in compiling and setting up software from source code. Here’s what each step does:

  • Configure:
    This step prepares the build environment. It checks system dependencies, verifies required libraries, and generates a customized build configuration (often via a Makefile or similar). It may also allow enabling/disabling specific features via options.
  • Build:
    This stage performs the actual compilation or assembly of the source code, producing executable binaries or libraries based on the configuration set in the previous step.
  • Install:
    After a successful build, this step copies the compiled files (executables, libraries, configuration files, etc.) to standard system directories (e.g., /usr/local/bin, /usr/lib), making the software ready for use.

In summary:
configure → prepares and adapts the build to your system;
build → compiles the code;
install → places the built software in the correct system locations.

A CMake project typically involves three stages:

configure

cmake --preset linux-debug

At this stage, all CMakeLists.txt and CMakePresets.json are read, compilers, third-party libraries, and variables are checked, and build system files are generated.

When using Ninja, files are generated under build/linux-debug at build.ninja.

build

cmake --build --preset linux-debug

At this stage, call Ninja to compile the .cpp files, generating build artifacts such as .o, .so, and executables.

install

cmake --install build/linux-debug

At this stage, follow the install(...) rule to copy the build artifacts to the installation directory.

In this template, Debug is installed to:

install/linux-debug/

Release installed to:

install/linux-release/

The typical structure after installation is as follows:

install/linux-debug/
├── bin/
│   └── cmake_template
├── include/
│   ├── lib1/
│   │   └── eigen3_test.hpp
│   └── lib2/
│       └── eigen3_test.hpp
└── lib64/
    ├── liblib1_src_lib.so
    └── liblib2_src_lib.so

Some Linux distributions use lib, while others use lib64. This template uses GNUInstallDirs to allow CMake to automatically select the appropriate directory.

Why not handwrite build/install/log scripts

This practice was common in older templates:

  1. Create your own build directory.
  2. Run cmake .. in build.
  3. Handwritten make install.
  4. Hand-written script sets up LD_LIBRARY_PATH.
  5. Maintain the log directory yourself.

After switching the template to use CMakePresets, this content is no longer needed:

previous methodNew Approach
Manual cd buildCMakePresets.json Unified Settings binaryDir
Manually set Debug / Releaseset CMAKE_BUILD_TYPE in the preset
Manually Set Installation Pathset CMAKE_INSTALL_PREFIX in the preset
Manually invoke Makefilepreset specifies Ninja
Manual setting of dynamic library pathsInstall Target Settings INSTALL_RPATH
Hand-writing VSCode Tasks

To manually create custom tasks in Visual Studio Code (VSCode), follow these steps:

1. Create the tasks.json File

  • Open your project folder in VSCode.
  • Press Ctrl+Shift+P (or Cmd+Shift+P on macOS) and type “Tasks: Configure Task”.
  • Select “Create tasks.json file from template” and choose “Others” (or start with an empty template).
  • This will generate a tasks.json file inside a .vscode folder in your project root.

2. Basic Structure of tasks.json

A minimal task configuration might look like this:

{  
  "version": "2.0.0",  
  "tasks": [  
    {  
      "label": "Build Project",  
      "type": "shell",  
      "command": "make",  
      "args": ["build"],  
      "group": {  
        "kind": "build",  
        "isDefault": true  
      }  
    }  
  ]  
}
  • label: A descriptive name for the task (visible in the task menu).
  • type: Usually "shell" (for command-line tasks) or "process" (for direct executables).
  • command: The command to run (e.g., make, gcc, python).
  • args: Arguments passed to the command.
  • group: Specifies the task type ("build", "test", etc.) and whether it’s the default.

3. Advanced Configuration

  • Dependencies: Use "dependsOn" to run multiple tasks in sequence.
    "dependsOn": ["Task1", "Task2"]
    
  • Problem Matchers: Map compiler output to VSCode’s Problems panel.
    "problemMatcher": "$gcc"
    
  • Environment Variables: Set via "env".
    "env": { "PYTHONPATH": "${workspaceFolder}/src" }
    
  • Input Variables: Prompt for user input dynamically.

4. Run Tasks

  • Press Ctrl+Shift+P and select “Tasks: Run Task”.
  • Choose your task from the list.
  • To run the default build task, use the shortcut Ctrl+Shift+B.

5. Examples for Common Scenarios

  • Compile C++ with g++:
    { "command": "g++", "args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}"] }
    
  • Run a Python script:
    { "command": "python", "args": ["${file}"] }
    
  • Execute a shell script:
    { "command": "./scripts/build.sh", "type": "shell" }
    

6. Tips

  • Use variables like ${workspaceFolder} or ${file} for dynamic paths.
  • Configure tasks per workspace (.vscode/tasks.json) or globally (user settings).
  • For complex builds, consider using compound tasks or integrating with build tools like CMake or Maven.

For full documentation, refer to VSCode’s official guide: Tasks in Visual Studio Code.|VSCode CMake Tools directly reads from presets.| |Handwriting complex debugging scripts|Directly run or debug the current CMake target using VSCode CMake Tools.|

Running and Debugging with VSCode CMake Tools

This template doesn't require extra VSCode debug configuration. After installing CMake Tools, VSCode can directly recognize executable targets in CMake and run or debug the current target.

Graphical processes can be understood as the button-based version of command-line processes:

command lineVSCode CMake Tools
cmake --preset linux-debugAfter selecting the Linux Debug preset, configure
cmake --build --preset linux-debugClick Build
./build/linux-debug/src/cmake_templateAfter selecting the cmake_template target, click the run button.
Debugging build artifacts with GDBAfter selecting the cmake_template target, click the Debug button.

CMake Tools typically runs or debugs the executable files located in the build directory:

build/linux-debug/src/cmake_template

Therefore, daily development does not require installation each time. install is primarily used for verifying the installation layout, header file installation, dynamic library RPATH, and similar aspects.

Recommended workflow:

  1. Install the VSCode CMake Tools extension and Microsoft C/C++ extension.
  2. Select linux-debug configure preset.
  3. Configure。
  4. Build。
  5. Select cmake_template as the run/debug target.
  6. Click the Run or Debug button provided by CMake Tools.

For more detailed graphical interface operation steps, see CMakePresets and Build and Installation.

  1. CMakePresets and Build Installation: First, understand the preset workflow.
  2. Top-level CMake and Public Compilation Options: Understanding the top-level entry point and public configuration.
  3. CMake In-Depth Guide for src and lib Modules: Understanding Executables, Libraries, Include, and Install.
  4. Third-party library dependency writing: Learn how to introduce libraries such as Eigen, OpenCV, Boost, and PCL.

After completing this tutorial, you should be able to:

  1. Create a new C++ project and build it using presets.
  2. Add new library directory.
  3. Add new executable files.
  4. Correctly manage the header file include paths.
  5. Import third-party libraries within the module.
  6. Once installed, run the program directly without needing to rely on hand-written scripts.
音乐页