CMake入门

CMake入门

CMake简介

CMake 是一个跨平台的构建系统生成工具,用于管理编译过程,其主要功能如下:

  • 通过读取配置文件(CMakeLists.txt
  • 生成特定于平台的构建文件(如 Makefile 或 Visual Studio 工程文件);
  • 帮助用户管理项目的依赖关系、编译流程等;

也就是说CMake是一套交叉编译的工具链,通过CMake我们可以更好地管理项目结构;

CMake 相比于传统的构建工具(如直接编写 Makefile),具有以下几个核心特性:

  • 跨平台:CMake 支持多个平台(如 WindowsLinuxmacOS)和编译器(GCCClangMSVC 等),通过生成平台特定的构建系统文件,能够帮助开发者更轻松地在不同平台上编译项目。

  • 依赖管理CMake 能够自动检测并配置项目的依赖库,确保在编译过程中正确地链接外部库。例如,它可以通过 find_package 指令查找和配置第三方库。

  • 增量编译CMake 支持增量编译机制。当源代码部分更新时,它只会重新编译受影响的部分,减少不必要的编译时间。

  • 模块化与可扩展性CMake 允许用户编写模块和脚本,用于管理更复杂的项目结构。它提供了丰富的指令集来配置编译流程,并且可以通过编写自定义的模块扩展其功能。

  • 可集成性CMake 可以生成多种构建工具的配置文件,支持不同的 IDE 和构建系统;

C++工程目录设计最佳实践

一个典型的 C++ 项目目录结构应该具有清晰的分层,常见的目录和文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/project-root
├── CMakeLists.txt # 项目的主 CMake 配置文件
├── src/ # 源文件目录
│ ├── main.cpp # 主程序入口
│ ├── module1.cpp # 模块1的源文件
│ ├── module2.cpp # 模块2的源文件
├── include/ # 公共头文件目录
│ ├── module1.h # 模块1的头文件
│ ├── module2.h # 模块2的头文件
├── lib/ # 外部库(静态或动态库)存放目录
├── bin/ # 存放二进制文件
├── tests/ # 测试代码
│ ├── CMakeLists.txt # 测试目录的 CMake 文件
│ ├── test_module1.cpp # 测试模块1的单元测试
├── examples/ # 示例代码
├── docs/ # 文档(用户指南、API 文档等)
├── build/ # 构建生成目录(通常不放入源码控制系统中)
├── scripts/ # 构建、部署、运行等自动化脚本
├── README.md # 项目说明文件
└── LICENSE # 项目许可证

除此之外,仍有一些细节需要注意:

  1. src/include/ 目录分离

    • src/ 中应该存放项目的 .cpp.c 文件,即项目的具体实现代码。

    • include/则存放公共的 .h.hpp 文件,这些头文件定义了接口,并且其他项目或模块可以包含这些头文件。

    • 使用这种分离策略有助于清晰地定义哪些文件是实现细节,哪些文件是对外暴露的接口;

    • 注意在头文件中不要using namespace std;;

  2. 模块化管理

    • 对于中大型项目,将代码划分为多个逻辑模块或库会使项目更具可维护性和可扩展性;

    • 每个模块通常有自己的源文件和头文件;

    • 每个模块应该尽量自包含,模块的头文件和实现文件可以放在各自的子目录下,例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /project-root
      ├── src/
      │ ├── module1/
      │ │ ├── module1.cpp
      │ │ └── CMakeLists.txt
      │ ├── module2/
      │ │ ├── module2.cpp
      │ │ └── CMakeLists.txt
      ├── include/
      │ ├── module1/
      │ │ └── module1.h
      │ ├── module2/
      │ │ └── module2.h
  3. 依赖管理

    • lib/ 目录:推荐通过自动化脚本或包管理工具自动下载和构建依赖,而非手动放入外部库的二进制文件;
    • third_party/ 目录:将外部依赖源代码放在 third_party/ 目录中,并使用 CMake 或其他工具集成;
  4. 测试目录结构

    • tests/ 目录应该用于存放所有的测试代码;
    • 推荐使用与项目主目录相同的模块化结构,保证测试代码清晰、独立,方便集成到 CI/CD 流程中
  5. 文档目录

    • docs/ 目录用于存放项目的文档资料,文档可以包含项目概述、API 文档、使用指南、开发者指南等。
    • 推荐使用 Doxygen并将其保存在此目录中;
  6. 构建输出目录

    • build/** 目录用于存放项目的构建输出文件(如中间文件、可执行文件等)。
    • 为了避免污染源码目录,建议将构建生成的文件放入单独的 build/ 目录中,并将此目录加入 .gitignore 中,防止其被加入版本控制;
  7. 使用脚本自动化流程

    • scripts/ 目录可以用于存放一些常用的自动化脚本,如构建、清理、测试、打包、发布等脚本。
    • 通常包括build.sh, test.sh, deploy.sh

CMake基本编译原理

CMake 的编译过程可以分为几个主要步骤:

  1. 配置阶段

    1. 读取 CMakeLists.txt 文件:
      • CMake 通过解析项目中的 CMakeLists.txt 文件来获取项目信息(源文件、依赖库、编译选项等);
    2. 生成构建系统:
      • ``CMake解析CMakeLists.txt` 后,基于当前平台和用户指定的生成器生成特定的构建系统文件这些生成的文件控制项目的编译流程;
  2. 生成阶段

    • CMake 根据配置阶段的结果,生成构建文件(如IDE工程文件),详细描述了如何从源代码生成目标文件;
    • CMake不直接编译代码,而是通过生成的构建系统文件调用具体的编译工具链来进行编译。
  3. 编译阶段

    • 用户运行生成的构建系统(例如执行 make 命令),调用编译器编译源代码,生成目标文件(如 .o 文件)。
    • 链接目标文件,生成最终的可执行文件或库文件(如 .exe.so/.dll 文件)。

简要来说,如果我的项目遵循上述的最佳实践的话,可以通过一个文件编写两条命令实现:

  • 编写CMakeLists.txt;
  • cmake .
  • cmake --build .

CMakeLists.txt编写原则

  1. 模块化:

    • 将项目配置划分为多个 CMake 文件;
    • 使用变量来避免重复代码,并通过函数/宏封装复杂的操作。
  2. 指定最低版本要求

    • 每个项目的 CMakeLists.txt 中明确指定所需的最低 CMake 版本;
  3. 项目声明

    • 定义项目名称和相关语言
  4. 管理编译选项

    • 设置编译器选项:通过 target_compile_options() 为目标指定编译器选项,而不是使用全局变量 CMAKE_CXX_FLAGS,这样可以避免全局配置导致的不必要副作用。
    • 使用 CMAKE_BUILD_TYPE:通过 CMAKE_BUILD_TYPE 设置构建模式(如 DebugRelease)。在实际项目中,建议允许用户在命令行中指定,而非硬编码在 CMakeLists.txt 中;
  5. 处理依赖项

    • 如果项目依赖于外部库,使用 find_package() 查找库,并根据需要设置是否强制要求该库;
    • 对于一些外部库,可以使用 FetchContent 下载和构建它们,避免手动配置;
  6. 管理源文件

    • 将源文件列表集中管理,并根据项目结构清晰划分。例如,可以将源文件分组,并使用 file() 或变量存储文件路径;
  7. 启用自动化测试

    • 建议为项目配置测试框架,并使用 CMake 的 enable_testing()add_test() 来集成测试
  8. 多平台支持

    • 检查系统特性:使用 if() 检查不同的平台或编译器选项,并在 CMakeLists.txt 中做出适应性调整。

    • 设置跨平台编译选项:使用 target_compile_options()target_link_libraries() 管理特定平台的编译选项;

  9. 支持安装和打包

    • 为了方便部署项目,可以在 CMakeLists.txt 中配置安装和打包规则。使用 install() 命令设置如何将生成的可执行文件、库和其他资源安装到系统路径;

CMakeLists.txt配置具体含义

指定CMake的最低版本

1
cmake_minimum_required(VERSION 3.15)

项目命名

1
project(MyDraft)

指定C++标准

1
set(CMAKE_CXX_STANDARD 20)

设置编译器路径

1
2
set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")

确保它们只在项目第一次运行时配置, 避免重复配置,或者即使清除CMake缓存;

设置源文件目录

1
file(GLOB SRC_LIST "${PROJECT_SOURCE_DIR}/src/tb/*.cpp")

添加头文件路径

1
include_directories(include)

生成目标可执行文件

1
add_executable(tb ${SRC_LIST})

外部库的引用

fmt库

从 CMake 3.11 开始,可以使用 FetchContent 自动 在 configure 时下载 {fmt} 作为依赖项:

1
2
3
4
5
6
7
8
9
include(FetchContent)

FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281) # 10.2.1
FetchContent_MakeAvailable(fmt)

target_link_libraries(<your-target> fmt::fmt)

注意<your-target>应填写可执行文件的名称

  • 在编译的最后一步,链接器会把编译后的目标文件(即 .o 文件或 .obj 文件)与所依赖的外部库(如静态库 .a 文件或动态库 .so/.dll 文件)链接在一起,生成最终的可执行文件。因此,target 通常是这个最终生成的可执行文件的名称;
  • 如果项目中包含多个不同的可执行文件,每个可执行文件可能会依赖不同的库。在这种情况下,你可以为每个目标定义一个不同的 target,以便生成多个不同的可执行文件;

在头文件内加入

1
2
3
4
5
6
7
8
9
10
11
12
# define FMT_HEADER_ONLY 	//  推荐
# include "fmt/core.h" // char UTF-8主要的格式化函数,支持C++20编译时检查,依赖最小化。
# include "fmt/format.h" // 完整的格式化API,除了额外的格式化函数之外,支持本地化(多语言支持)。
# include "fmt/ranges.h" // 格式化ranges 和 tuples
# include "fmt/chrono.h" // 日期和时间的格式化。
# include "fmt/std.h" // c++标准库类型的格式化支持。
# include "fmt/compile." // 格式化字符串的编译 (编译时格式化字符串检测)。FMT_STRING(s)
# include "fmt/color.h" // 端颜色和文本样式。
# include "fmt/os.h" // 提供系API。
# include "fmt/ostream." // 支持std::ostream。
# include "fmt/printf.h" // 支持printf格式化。
# include "fmt/xchar.h" // 选的wchar_t支持。

注意

相对路径问题

通常,工作目录是在命令行中运行可执行文件的地方;

如果设置bin/为输出目录,那么需要一个上级目录..才能回到项目根目录;

对于头文件的相对路径,通常会设置include_directories(include),直接引用对应模块即可;

示例

以下是一个CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 指定`CMake`的最低版本
cmake_minimum_required(VERSION 3.15)

# 项目命名
project(MyDraft)

# 指定 C++ 标准
set(CMAKE_CXX_STANDARD 20)

# 设置编译器路径
# set(CMAKE_C_COMPILER "/usr/bin/gcc")
# set(CMAKE_CXX_COMPILER "/usr/bin/g++")

# 设置可执行文件的输出目录
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")

# 添加头文件路径
include_directories(include)

# 设置源文件目录
file(GLOB SRC_LIST "${PROJECT_SOURCE_DIR}/src/hello/*.cpp")

# 生成目标可执行文件
add_executable(hello ${SRC_LIST})

# 引入外部库
include(FetchContent)

FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281) # 10.2.1
FetchContent_MakeAvailable(fmt)

target_link_libraries(hello fmt::fmt)

我的目录树如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
.MyCMake
├── bin
│ └── hello
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ │ ├── 3.30.3
│ │ │ ├── CMakeCCompiler.cmake
│ │ │ ├── CMakeCXXCompiler.cmake
│ │ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ │ ├── CMakeSystem.cmake
│ │ │ ├── CompilerIdC
│ │ │ │ ├── a.out
│ │ │ │ ├── CMakeCCompilerId.c
│ │ │ │ └── tmp
│ │ │ └── CompilerIdCXX
│ │ │ ├── a.out
│ │ │ ├── CMakeCXXCompilerId.cpp
│ │ │ └── tmp
│ │ ├── cmake.check_cache
│ │ ├── CMakeConfigureLog.yaml
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── CMakeScratch
│ │ ├── hello.dir
│ │ │ ├── build.make
│ │ │ ├── cmake_clean.cmake
│ │ │ ├── compiler_depend.make
│ │ │ ├── compiler_depend.ts
│ │ │ ├── DependInfo.cmake
│ │ │ ├── depend.make
│ │ │ ├── flags.make
│ │ │ ├── link.txt
│ │ │ ├── progress.make
│ │ │ └── src
│ │ │ └── hello
│ │ │ ├── hello.cpp.o
│ │ │ └── hello.cpp.o.d
│ │ ├── Makefile2
│ │ ├── Makefile.cmake
│ │ ├── pkgRedirects
│ │ ├── progress.marks
│ │ └── TargetDirectories.txt
│ ├── cmake_install.cmake
│ ├── _deps
│ │ ├── fmt-build
│ │ │ ├── CMakeFiles
│ │ │ │ ├── CMakeDirectoryInformation.cmake
│ │ │ │ ├── Export
│ │ │ │ │ └── b834597d9b1628ff12ae4314c3a2e4b8
│ │ │ │ │ ├── fmt-targets.cmake
│ │ │ │ │ └── fmt-targets-noconfig.cmake
│ │ │ │ ├── fmt.dir
│ │ │ │ │ ├── build.make
│ │ │ │ │ ├── cmake_clean.cmake
│ │ │ │ │ ├── cmake_clean_target.cmake
│ │ │ │ │ ├── compiler_depend.make
│ │ │ │ │ ├── compiler_depend.ts
│ │ │ │ │ ├── DependInfo.cmake
│ │ │ │ │ ├── depend.make
│ │ │ │ │ ├── flags.make
│ │ │ │ │ ├── link.txt
│ │ │ │ │ ├── progress.make
│ │ │ │ │ └── src
│ │ │ │ │ ├── format.cc.o
│ │ │ │ │ ├── format.cc.o.d
│ │ │ │ │ ├── os.cc.o
│ │ │ │ │ └── os.cc.o.d
│ │ │ │ └── progress.marks
│ │ │ ├── cmake_install.cmake
│ │ │ ├── fmt-config.cmake
│ │ │ ├── fmt-config-version.cmake
│ │ │ ├── fmt.pc
│ │ │ ├── fmt-targets.cmake
│ │ │ ├── libfmt.a
│ │ │ └── Makefile
│ │ ├── fmt-src
│ │ │ ├── ChangeLog.md
│ │ │ ├── CMakeLists.txt
│ │ │ ├── CONTRIBUTING.md
│ │ │ ├── doc
│ │ │ │ ├── api.rst
│ │ │ │ ├── basic-bootstrap
│ │ │ │ │ ├── layout.html
│ │ │ │ │ ├── README
│ │ │ │ │ └── theme.conf
│ │ │ │ ├── bootstrap
│ │ │ │ │ ├── alerts.less
│ │ │ │ │ ├── badges.less
│ │ │ │ │ ├── bootstrap.less
│ │ │ │ │ ├── breadcrumbs.less
│ │ │ │ │ ├── button-groups.less
│ │ │ │ │ ├── buttons.less
│ │ │ │ │ ├── carousel.less
│ │ │ │ │ ├── close.less
│ │ │ │ │ ├── code.less
│ │ │ │ │ ├── component-animations.less
│ │ │ │ │ ├── dropdowns.less
│ │ │ │ │ ├── forms.less
│ │ │ │ │ ├── glyphicons.less
│ │ │ │ │ ├── grid.less
│ │ │ │ │ ├── input-groups.less
│ │ │ │ │ ├── jumbotron.less
│ │ │ │ │ ├── labels.less
│ │ │ │ │ ├── list-group.less
│ │ │ │ │ ├── media.less
│ │ │ │ │ ├── mixins
│ │ │ │ │ │ ├── alerts.less
│ │ │ │ │ │ ├── background-variant.less
│ │ │ │ │ │ ├── border-radius.less
│ │ │ │ │ │ ├── buttons.less
│ │ │ │ │ │ ├── center-block.less
│ │ │ │ │ │ ├── clearfix.less
│ │ │ │ │ │ ├── forms.less
│ │ │ │ │ │ ├── gradients.less
│ │ │ │ │ │ ├── grid-framework.less
│ │ │ │ │ │ ├── grid.less
│ │ │ │ │ │ ├── hide-text.less
│ │ │ │ │ │ ├── image.less
│ │ │ │ │ │ ├── labels.less
│ │ │ │ │ │ ├── list-group.less
│ │ │ │ │ │ ├── nav-divider.less
│ │ │ │ │ │ ├── nav-vertical-align.less
│ │ │ │ │ │ ├── opacity.less
│ │ │ │ │ │ ├── pagination.less
│ │ │ │ │ │ ├── panels.less
│ │ │ │ │ │ ├── progress-bar.less
│ │ │ │ │ │ ├── reset-filter.less
│ │ │ │ │ │ ├── resize.less
│ │ │ │ │ │ ├── responsive-visibility.less
│ │ │ │ │ │ ├── size.less
│ │ │ │ │ │ ├── tab-focus.less
│ │ │ │ │ │ ├── table-row.less
│ │ │ │ │ │ ├── text-emphasis.less
│ │ │ │ │ │ ├── text-overflow.less
│ │ │ │ │ │ └── vendor-prefixes.less
│ │ │ │ │ ├── mixins.less
│ │ │ │ │ ├── modals.less
│ │ │ │ │ ├── navbar.less
│ │ │ │ │ ├── navs.less
│ │ │ │ │ ├── normalize.less
│ │ │ │ │ ├── pager.less
│ │ │ │ │ ├── pagination.less
│ │ │ │ │ ├── panels.less
│ │ │ │ │ ├── popovers.less
│ │ │ │ │ ├── print.less
│ │ │ │ │ ├── progress-bars.less
│ │ │ │ │ ├── responsive-embed.less
│ │ │ │ │ ├── responsive-utilities.less
│ │ │ │ │ ├── scaffolding.less
│ │ │ │ │ ├── tables.less
│ │ │ │ │ ├── theme.less
│ │ │ │ │ ├── thumbnails.less
│ │ │ │ │ ├── tooltip.less
│ │ │ │ │ ├── type.less
│ │ │ │ │ ├── utilities.less
│ │ │ │ │ ├── variables.less
│ │ │ │ │ └── wells.less
│ │ │ │ ├── build.py
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── conf.py
│ │ │ │ ├── contents.rst
│ │ │ │ ├── fmt.less
│ │ │ │ ├── index.rst
│ │ │ │ ├── python-license.txt
│ │ │ │ ├── _static
│ │ │ │ │ ├── bootstrap.min.js
│ │ │ │ │ ├── breathe.css
│ │ │ │ │ └── fonts
│ │ │ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ │ │ └── glyphicons-halflings-regular.woff
│ │ │ │ ├── syntax.rst
│ │ │ │ ├── _templates
│ │ │ │ │ ├── layout.html
│ │ │ │ │ └── search.html
│ │ │ │ └── usage.rst
│ │ │ ├── include
│ │ │ │ └── fmt
│ │ │ │ ├── args.h
│ │ │ │ ├── chrono.h
│ │ │ │ ├── color.h
│ │ │ │ ├── compile.h
│ │ │ │ ├── core.h
│ │ │ │ ├── format.h
│ │ │ │ ├── format-inl.h
│ │ │ │ ├── os.h
│ │ │ │ ├── ostream.h
│ │ │ │ ├── printf.h
│ │ │ │ ├── ranges.h
│ │ │ │ ├── std.h
│ │ │ │ └── xchar.h
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── fmt.cc
│ │ │ │ ├── format.cc
│ │ │ │ └── os.cc
│ │ │ ├── support
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── Android.mk
│ │ │ │ ├── bazel
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── README.md
│ │ │ │ │ └── WORKSPACE.bazel
│ │ │ │ ├── build-docs.py
│ │ │ │ ├── build.gradle
│ │ │ │ ├── cmake
│ │ │ │ │ ├── FindSetEnv.cmake
│ │ │ │ │ ├── fmt-config.cmake.in
│ │ │ │ │ ├── fmt.pc.in
│ │ │ │ │ └── JoinPaths.cmake
│ │ │ │ ├── compute-powers.py
│ │ │ │ ├── C++.sublime-syntax
│ │ │ │ ├── docopt.py
│ │ │ │ ├── manage.py
│ │ │ │ ├── printable.py
│ │ │ │ ├── README
│ │ │ │ ├── rtd
│ │ │ │ │ ├── conf.py
│ │ │ │ │ ├── index.rst
│ │ │ │ │ └── theme
│ │ │ │ │ ├── layout.html
│ │ │ │ │ └── theme.conf
│ │ │ │ └── Vagrantfile
│ │ │ └── test
│ │ │ ├── add-subdirectory-test
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ └── main.cc
│ │ │ ├── args-test.cc
│ │ │ ├── assert-test.cc
│ │ │ ├── chrono-test.cc
│ │ │ ├── CMakeLists.txt
│ │ │ ├── color-test.cc
│ │ │ ├── compile-error-test
│ │ │ │ └── CMakeLists.txt
│ │ │ ├── compile-fp-test.cc
│ │ │ ├── compile-test.cc
│ │ │ ├── core-test.cc
│ │ │ ├── cuda-test
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── cpp14.cc
│ │ │ │ └── cuda-cpp14.cu
│ │ │ ├── detect-stdfs.cc
│ │ │ ├── enforce-checks-test.cc
│ │ │ ├── find-package-test
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ └── main.cc
│ │ │ ├── format-impl-test.cc
│ │ │ ├── format-test.cc
│ │ │ ├── fuzzing
│ │ │ │ ├── build.sh
│ │ │ │ ├── chrono-duration.cc
│ │ │ │ ├── chrono-timepoint.cc
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── float.cc
│ │ │ │ ├── fuzzer-common.h
│ │ │ │ ├── main.cc
│ │ │ │ ├── named-arg.cc
│ │ │ │ ├── one-arg.cc
│ │ │ │ ├── README.md
│ │ │ │ └── two-args.cc
│ │ │ ├── gtest
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── gmock
│ │ │ │ │ └── gmock.h
│ │ │ │ ├── gmock-gtest-all.cc
│ │ │ │ └── gtest
│ │ │ │ ├── gtest.h
│ │ │ │ └── gtest-spi.h
│ │ │ ├── gtest-extra.cc
│ │ │ ├── gtest-extra.h
│ │ │ ├── gtest-extra-test.cc
│ │ │ ├── header-only-test.cc
│ │ │ ├── mock-allocator.h
│ │ │ ├── module-test.cc
│ │ │ ├── noexception-test.cc
│ │ │ ├── os-test.cc
│ │ │ ├── ostream-test.cc
│ │ │ ├── posix-mock.h
│ │ │ ├── posix-mock-test.cc
│ │ │ ├── printf-test.cc
│ │ │ ├── ranges-odr-test.cc
│ │ │ ├── ranges-test.cc
│ │ │ ├── scan.h
│ │ │ ├── scan-test.cc
│ │ │ ├── static-export-test
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── library.cc
│ │ │ │ └── main.cc
│ │ │ ├── std-test.cc
│ │ │ ├── test-assert.h
│ │ │ ├── test-main.cc
│ │ │ ├── unicode-test.cc
│ │ │ ├── util.cc
│ │ │ ├── util.h
│ │ │ └── xchar-test.cc
│ │ └── fmt-subbuild
│ │ ├── CMakeCache.txt
│ │ ├── CMakeFiles
│ │ │ ├── 3.30.3
│ │ │ │ └── CMakeSystem.cmake
│ │ │ ├── cmake.check_cache
│ │ │ ├── CMakeConfigureLog.yaml
│ │ │ ├── CMakeDirectoryInformation.cmake
│ │ │ ├── CMakeRuleHashes.txt
│ │ │ ├── fmt-populate-complete
│ │ │ ├── fmt-populate.dir
│ │ │ │ ├── build.make
│ │ │ │ ├── cmake_clean.cmake
│ │ │ │ ├── compiler_depend.make
│ │ │ │ ├── compiler_depend.ts
│ │ │ │ ├── DependInfo.cmake
│ │ │ │ ├── Labels.json
│ │ │ │ ├── Labels.txt
│ │ │ │ └── progress.make
│ │ │ ├── Makefile2
│ │ │ ├── Makefile.cmake
│ │ │ ├── pkgRedirects
│ │ │ ├── progress.marks
│ │ │ └── TargetDirectories.txt
│ │ ├── cmake_install.cmake
│ │ ├── CMakeLists.txt
│ │ ├── fmt-populate-prefix
│ │ │ ├── src
│ │ │ │ └── fmt-populate-stamp
│ │ │ │ ├── fmt-populate-build
│ │ │ │ ├── fmt-populate-configure
│ │ │ │ ├── fmt-populate-done
│ │ │ │ ├── fmt-populate-download
│ │ │ │ ├── fmt-populate-gitclone-lastrun.txt
│ │ │ │ ├── fmt-populate-gitinfo.txt
│ │ │ │ ├── fmt-populate-install
│ │ │ │ ├── fmt-populate-mkdir
│ │ │ │ ├── fmt-populate-patch
│ │ │ │ ├── fmt-populate-patch-info.txt
│ │ │ │ ├── fmt-populate-test
│ │ │ │ └── fmt-populate-update-info.txt
│ │ │ └── tmp
│ │ │ ├── fmt-populate-cfgcmd.txt
│ │ │ ├── fmt-populate-gitclone.cmake
│ │ │ ├── fmt-populate-gitupdate.cmake
│ │ │ └── fmt-populate-mkdirs.cmake
│ │ └── Makefile
│ └── Makefile
├── CMakeLists.txt
├── doc
│ └── CMake入门.md
├── include
│ └── hello
│ └── hello.hpp
├── lib
└── src
└── hello
└── hello.cpp
61 directories, 294 files

我编写了一个引用外部库fmt输出hello,world的程序src/hello/hello.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# include "hello/hello.hpp"

void stdSayHello(int i){
while(i--) fmt::print("hello world_{}\n", i);
}

void fmtSayHello(int i){
while(i--) std::cout<< "hello world_"<<i<<std::endl;
}

int main(){
stdSayHello(3);
fmtSayHello(3);
}

include/中,头文件include/hello/hello.hpp也很简单:

1
2
3
4
# include <iostream>

# define FMT_HEADER_ONLY // 推荐
# include "fmt/core.h"

cd build & cmake .. & cmake --build .执行便在bin/生成了可执行文件,这表明我们编译成功了!