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 的核心用法。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐