从零到精通:CMake实战指南
CMake 是 Linux 下 C++ 开发的必备工具,能够轻松管理单文件、多文件乃至大型模块化项目,解决编译依赖、跨平台等核心问题。本文从零基础入门,一步步带你掌握 CMake 基础用法,再通过经典开源项目拆解进阶技巧,新手也能快速上手、逐步提升!
一、前置准备:安装必要工具(必做)
CMake 编译依赖 gcc、g++ 等编译器,先确保系统已安装,不同 Linux 发行版命令略有差异,按需执行即可。
1. 安装命令
Ubuntu/Debian 系列(如 Ubuntu 20.04/22.04):
sudo apt update
sudo apt install cmake g++ gcc build-essential
CentOS/RHEL 系列:
sudo yum install cmake gcc gcc-c++ make
2. 验证安装成功
安装完成后,在终端输入以下命令,能显示版本号即说明安装成功:
cmake --version
g++ --version

二、编写核心文件(规范结构,便于后续扩展)
建议遵循 CMake 常规项目结构,这样后续扩展多文件、多模块时,文件管理更清晰,避免出现文件混乱、依赖报错的问题,同时也符合行业常规开发规范。
1. 新建项目文件夹
在 VSCode 中新建一个文件夹,作为项目根目录,打开该文件夹。

2. 项目实例
打开终端,输入:
touch main.cpp CMakeLists.txt
(1). 编写 main.cpp
#include <iostream>
using namespace std;
int main() {
cout << "HELLO,WORLD!" << endl;
return 0;
}
(2).编写 CMakeLists.txt
# CMake 版本要求
cmake_minimum_required(VERSION 3.10)
# 项目名称(可自定义,比如改成自己的项目名)
project(demo1)
# 指定 C++ 标准(这里用 C++11,兼容大部分场景,可按需修改为 14/17)
set(CMAKE_CXX_STANDARD 11)
# 生成可执行文件(格式:add_executable(可执行文件名 源代码文件))
add_executable(demo main.cpp)
三、编译代码文件
以下分别介绍单文件、多文件的编译步骤,两种场景的核心编译流程一致,仅多文件需额外新增相关文件并修改配置,新手可先从单文件入手,熟悉后再尝试多文件。
(一)单文件编译
1. 创建编译文件夹并进入:
mkdir build
cd build
2. 生成 Makefile(CMake 核心步骤,读取上级目录的 CMakeLists.txt):
3. 编译代码(生成可执行文件):
4. 运行生成的程序(可执行文件名为 CMakeLists.txt 中定义的 ):
(一)多文件编译
1.创建多个文件

(1). 编写 test.cpp
#include "test.hpp" // 引入对应头文件
void demo() {
cout << "多文件项目配置成功!" << endl;
}
int add(int a, int b) {
return a + b;
}
(2).编写test.hpp
#ifndef TEST_HPP // 防止头文件重复包含
#define TEST_HPP
#include <iostream>
using namespace std;
void demo();
int add(int a, int b);
#endif
(3)main.cpp
#include <iostream>
#include "test.hpp" // 引入自定义头文件
using namespace std;
int main() {
demo();
int result = add(10, 20);
cout << "10 + 20 = " << result << endl;
return 0;
}
(4)CMakeLists.txt:在add_executable 一行,追加新增的源文件
add_executable(demo main.cpp test.cpp)
2.同(1)编译步骤


四、进阶:上项目讲解cmake的进阶技能
掌握基础用法后,我们通过一个经典开源项目,学习大型项目中 CMake 的进阶用法,快速提升工程化能力, 所参考的项目:https://github.com/ttroy50/cmake-examples
1.把项目git下来

如何操作,可以学习:https://blog.csdn.net/2403_89166179/article/details/159648937?spm=1001.2014.3001.5501
2.项目文件结构
w@w:~/cmake-examples$ tree
.
├── 01-basic
│ ├── A-hello-cmake
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ └── README.adoc
│ ├── B-hello-headers
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── Hello.h
│ │ ├── README.adoc
│ │ └── src
│ │ ├── Hello.cpp
│ │ └── main.cpp
│ ├── C-static-library
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── static
│ │ │ └── Hello.h
│ │ ├── README.adoc
│ │ └── src
│ │ ├── Hello.cpp
│ │ └── main.cpp
│ ├── D-shared-library
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── shared
│ │ │ └── Hello.h
│ │ ├── README.adoc
│ │ └── src
│ │ ├── Hello.cpp
│ │ └── main.cpp
│ ├── E-installing
│ │ ├── cmake-examples.conf
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── installing
│ │ │ └── Hello.h
│ │ ├── README.adoc
│ │ └── src
│ │ ├── Hello.cpp
│ │ └── main.cpp
│ ├── F-build-type
│ │ ├── cmake-gui-build-type.png
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ └── README.adoc
│ ├── G-compile-flags
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ └── README.adoc
│ ├── H-third-party-library
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ └── README.adoc
│ ├── I-compiling-with-clang
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── pre_test.sh
│ │ ├── README.adoc
│ │ └── run_test.sh
│ ├── J-building-with-ninja
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── pre_test.sh
│ │ ├── README.adoc
│ │ └── run_test.sh
│ ├── K-imported-targets
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── README.adoc
│ │ └── run_test.sh
│ ├── L-cpp-standard
│ │ ├── i-common-method
│ │ │ ├── CMakeLists.txt
│ │ │ ├── main.cpp
│ │ │ └── README.adoc
│ │ ├── ii-cxx-standard
│ │ │ ├── CMakeLists.txt
│ │ │ ├── main.cpp
│ │ │ └── README.adoc
│ │ ├── iii-compile-features
│ │ │ ├── CMakeLists.txt
│ │ │ ├── main.cpp
│ │ │ └── README.adoc
│ │ └── README.adoc
│ └── README.adoc
├── 02-sub-projects
│ ├── A-basic
│ │ ├── CMakeLists.txt
│ │ ├── README.adoc
│ │ ├── subbinary
│ │ │ ├── CMakeLists.txt
│ │ │ └── main.cpp
│ │ ├── sublibrary1
│ │ │ ├── CMakeLists.txt
│ │ │ ├── include
│ │ │ │ └── sublib1
│ │ │ │ └── sublib1.h
│ │ │ └── src
│ │ │ └── sublib1.cpp
│ │ └── sublibrary2
│ │ ├── CMakeLists.txt
│ │ └── include
│ │ └── sublib2
│ │ └── sublib2.h
│ └── README.adoc
├── 03-code-generation
│ ├── configure-files
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── path.h.in
│ │ ├── README.adoc
│ │ └── ver.h.in
│ ├── protobuf
│ │ ├── AddressBook.proto
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ └── README.adoc
│ └── README.adoc
├── 04-static-analysis
│ ├── clang-analyzer
│ │ ├── CMakeLists.txt
│ │ ├── README.adoc
│ │ ├── run_test.sh
│ │ ├── subproject1
│ │ │ ├── CMakeLists.txt
│ │ │ └── main1.cpp
│ │ └── subproject2
│ │ ├── CMakeLists.txt
│ │ └── main2.cpp
│ ├── clang-format
│ │ ├── cmake
│ │ │ ├── modules
│ │ │ │ ├── clang-format.cmake
│ │ │ │ └── FindClangFormat.cmake
│ │ │ └── scripts
│ │ │ ├── clang-format-check-changed
│ │ │ └── clang-format-check-changed.py
│ │ ├── CMakeLists.txt
│ │ ├── README.adoc
│ │ ├── run_test.sh
│ │ ├── subproject1
│ │ │ ├── CMakeLists.txt
│ │ │ └── main1.cpp
│ │ └── subproject2
│ │ ├── CMakeLists.txt
│ │ └── main2.cpp
│ ├── cppcheck
│ │ ├── cmake
│ │ │ ├── analysis.cmake
│ │ │ └── modules
│ │ │ └── FindCppCheck.cmake
│ │ ├── CMakeLists.txt
│ │ ├── README.adoc
│ │ ├── run_test.sh
│ │ ├── subproject1
│ │ │ ├── CMakeLists.txt
│ │ │ └── main1.cpp
│ │ └── subproject2
│ │ ├── CMakeLists.txt
│ │ └── main2.cpp
│ ├── cppcheck-compile-commands
│ │ ├── cmake
│ │ │ └── modules
│ │ │ └── FindCppCheck.cmake
│ │ ├── CMakeLists.txt
│ │ ├── README.adoc
│ │ ├── run_test.sh
│ │ ├── subproject1
│ │ │ ├── CMakeLists.txt
│ │ │ └── main1.cpp
│ │ └── subproject2
│ │ ├── CMakeLists.txt
│ │ └── main2.cpp
│ └── README.adoc
├── 05-unit-testing
│ ├── boost
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── Palindrome.cpp
│ │ ├── Palindrome.h
│ │ ├── post_test.sh
│ │ ├── README.adoc
│ │ ├── Reverse.cpp
│ │ ├── Reverse.h
│ │ └── unit_tests.cpp
│ ├── catch2-vendored
│ │ ├── 3rd_party
│ │ │ └── catch2
│ │ │ ├── catch2
│ │ │ │ └── catch.hpp
│ │ │ └── CMakeLists.txt
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── Palindrome.cpp
│ │ ├── Palindrome.h
│ │ ├── post_test.sh
│ │ ├── README.adoc
│ │ ├── Reverse.cpp
│ │ ├── Reverse.h
│ │ └── unit_tests.cpp
│ ├── google-test-download
│ │ ├── 3rd_party
│ │ │ └── google-test
│ │ │ ├── CMakeLists.txt
│ │ │ └── CMakeLists.txt.in
│ │ ├── CMakeLists.txt
│ │ ├── main.cpp
│ │ ├── Palindrome.cpp
│ │ ├── Palindrome.h
│ │ ├── post_test.sh
│ │ ├── README.adoc
│ │ ├── Reverse.cpp
│ │ ├── Reverse.h
│ │ ├── run_test.sh
│ │ └── unit_tests.cpp
│ └── README.adoc
├── 06-installer
│ ├── deb
│ │ ├── cmake-examples.conf
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── Hello.h
│ │ ├── post_test.sh
│ │ ├── README.adoc
│ │ └── src
│ │ ├── Hello.cpp
│ │ └── main.cpp
│ └── README.adoc
├── 07-package-management
│ ├── A-using-system-provide-packages
│ │ └── README.adoc
│ ├── B-vendoring-code
│ │ └── README.adoc
│ ├── C-external-project-add
│ │ └── README.adoc
│ ├── D-conan
│ │ ├── i-basic
│ │ │ ├── CMakeLists.txt
│ │ │ ├── conanfile.txt
│ │ │ ├── main.cpp
│ │ │ ├── README.adoc
│ │ │ └── run_test.sh
│ │ ├── ii-basic-targets
│ │ │ ├── CMakeLists.txt
│ │ │ ├── conanfile.txt
│ │ │ ├── main.cpp
│ │ │ ├── README.adoc
│ │ │ └── run_test.sh
│ │ └── README.adoc
│ └── README.adoc
├── cmake-examples.sublime-project
├── dockerfiles
│ ├── README.adoc
│ ├── setup.sh
│ ├── ubuntu14.04-cmake-3.4.3
│ ├── ubuntu14.04-default-2.8.12.2
│ ├── ubuntu16.04-cmake-3.10.3
│ └── ubuntu16.04-default-cmake-3.5.1
├── LICENSE
├── README.adoc
└── test.sh
82 directories, 181 files
3.核心示例拆解
我们挑选项目中最实用、最贴近日常开发的3个核心场景
场景1:子项目/多模块管理(02-sub-projects)
对应项目目录:02-sub-projects/A-basic/,核心解决“大型项目多模块拆分”问题
(1)顶层 CMakeLists.txt:
cmake_minimum_required (VERSION 3.5)
project(subprojects)
# 关键1:添加子模块(sublibrary1、sublibrary2、subbinary)
# 每个子模块必须有自己的CMakeLists.txt,按“功能”拆分
add_subdirectory(sublibrary1)
add_subdirectory(sublibrary2)
add_subdirectory(subbinary)
(2)sublibrary1/CMakeLists.txt(子模块生成静态库,供可执行模块调用):
# Set the project name
project (sublibrary1)
# 生成静态库(sublibrary1),源文件是src/sublib1.cpp
add_library(${PROJECT_NAME} src/sublib1.cpp)
add_library(sub::lib1 ALIAS ${PROJECT_NAME})
# 关键2:导出头文件路径,供其他模块(如subbinary)调用
target_include_directories( ${PROJECT_NAME}
PUBLIC # 公开头文件,允许其他target包含
${PROJECT_SOURCE_DIR}/include # 头文件放在include目录,规范命名
)
(3)subbinary/CMakeLists.txt(可执行模块链接子模块库):
project(subbinary)
# Create the executable
add_executable(${PROJECT_NAME} main.cpp)
# Link the static library from subproject1 using its alias sub::lib1
# Link the header only library from subproject2 using its alias sub::lib2
# This will cause the include directories for that target to be added to this project
# 关键3:链接子模块生成的静态库(sublibrary1)和头文件模块(sublibrary2)
target_link_libraries(${PROJECT_NAME}
sub::lib1
sub::lib2
)
场景2:静态库/动态库生成与链接(01-basic)
对应项目目录:01-basic/C-static-library/、01-basic/D-shared-library/
(1)静态库生成(01-basic/C-static-library)
cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
# 生成静态库(hello_static),源文件是src/Hello.cpp
# 语法:add_library(库名 库类型 源文件)
add_library(hello_library STATIC
src/Hello.cpp
)
# 导出头文件路径(规范:头文件放在include/static/下)
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(hello_binary
src/main.cpp
)
# 生成可执行文件,链接静态库
target_link_libraries( hello_binary
PRIVATE
hello_library
)
(2)动态库生成(01-basic/D-shared-library)
cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
# 生成动态库(hello_shared),源文件是src/Hello.cpp
add_library(hello_library SHARED
src/Hello.cpp
)
add_library(hello::library ALIAS hello_library)
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(hello_binary
src/main.cpp
)
# link the new hello_library target with the hello_binary target
target_link_libraries( hello_binary
PRIVATE
hello::library
)
(3)区分
- 静态库:编译时将库代码嵌入可执行文件,可执行文件体积大,但运行时不依赖外部库,部署方便;cmake-examples 中静态库头文件放在 include/static/,规范清晰;
- 动态库:编译时仅记录库的引用,可执行文件体积小,多个程序可共享一个动态库,但运行时需确保动态库存在(避免“找不到.so文件”报错);头文件放在 include/shared/,与静态库区分;
场景3:包管理(07-package-management)
对应项目目录:07-package-management/D-conan/
(1)Conan管理fmt库(i-basic)
cmake_minimum_required(VERSION 3.5)
project (conan_third_party_include)
set(CMAKE_CXX_STANDARD 11)
# 关键1:引入Conan包管理模块(需先安装Conan)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
# Add an executable
add_executable(third_party_include main.cpp)
# 关键2:链接Conan下载的第三方库(fmt)
# Conan会自动处理库路径和头文件路径,无需手动指定
target_link_libraries(third_party_include
PRIVATE
${CONAN_LIBS}
)
五、结束语
本文从基础工具安装、单文件/多文件编译,到项目进阶技巧,一步步带你入门 CMake。所参考的 cmake-examples 项目还有很多实用场景(如代码生成、单元测试、静态分析等),剩余内容大家可以自行克隆项目学习,多动手编译运行,就能彻底掌握 CMake 的核心用法。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)