01 Cmake使用
感谢up主 双笙子佯谬:https://space.bilibili.com/263032155
学C++从Cmake开始
1. 简介和基础概念
-
CMake 简介和优势:
- CMake 是一个跨平台的开源构建系统生成工具,用于管理软件构建过程中的编译、链接和安装步骤。
- 主要优势包括跨平台性(能够在多种操作系统上生成本地构建文件)、简化构建过程、与各种编译器和工具链的兼容性。
-
CMakeLists.txt 文件结构和基本语法:
cmake_minimum_required
:指定运行该 CMakeLists.txt 所需的最低 CMake 版本。project
命令:定义项目名称和支持的语言。
2. 构建目标和管理
-
创建和管理构建目标:
- 使用
add_executable
命令创建可执行文件目标。 - 使用
add_library
命令创建静态或动态库目标。 - 示例:
add_executable(myapp main.cpp) add_library(mylib STATIC lib1.cpp lib2.cpp)
- 使用
-
链接库文件:
- 使用
target_link_libraries
命令将库文件链接到目标。 - 示例:
target_link_libraries(myapp PRIVATE mylib)
- 使用
3. 多文件项目管理
-
组织和管理多个源文件:
- 使用
add_subdirectory
命令添加子目录并管理子项目。 - 示例:
add_subdirectory(submodule)
- 使用
-
合理组织头文件和源文件:
- 确保头文件和源文件的适当组织,使项目结构清晰和可维护性高。
4. 编译选项和宏定义
-
添加编译器选项:
- 使用
target_compile_options
命令添加特定的编译器选项。 - 示例:
target_compile_options(myapp PRIVATE -Wall -O2)
- 使用
-
定义预处理器宏:
- 使用
target_compile_definitions
命令定义预处理器宏。 - 示例:
target_compile_definitions(myapp PRIVATE MY_MACRO)
- 使用
5. 第三方库的集成
-
纯头文件库的集成和使用:
- 将纯头文件库的 include 目录添加到项目中。
- 示例:
include_directories(path/to/library/include)
-
使用外部库:
- 使用
find_package
查找系统安装的库并集成到项目中。 - 示例:
find_package(Boost REQUIRED) target_include_directories(myapp PRIVATE ${Boost_INCLUDE_DIRS}) target_link_libraries(myapp PRIVATE ${Boost_LIBRARIES})
- 使用
6. 高级功能和技巧
-
自定义构建过程:
- 使用自定义命令和自定义目标以实现特定的构建需求。
- 示例:
add_custom_command(OUTPUT output_file COMMAND command_to_generate_output DEPENDS input_file)
-
特定平台和编译器的处理:
- 根据不同的平台和编译器设置不同的选项和路径。
- 示例:
if (UNIX) target_compile_options(myapp PRIVATE -pthread) endif()
7. 实战案例和最佳实践

CMakeLists.txt
# 设置 CMake 最低版本要求为 3.12
cmake_minimum_required(VERSION 3.12)
# 定义项目名称为 hellocmake,并指定使用的编程语言为 C++ (CXX)
project(hellocmake LANGUAGES CXX)
# 添加子目录 hellolib和mathlib,执行其中的 CMakeLists.txt 文件
add_subdirectory(hellolib)
add_subdirectory(mathlib)
# 创建可执行文件 a.out,将 main.cpp 添加为源文件
add_executable(a.out main.cpp)
# 链接 hellolib mathlib 库到 a.out 可执行文件,使用 PUBLIC 修饰符表示 hellolib mathlib的依赖关系会传播到 a.out
target_link_libraries(a.out PUBLIC hellolib mathlib)
hellolib/CMakeLists.txt
# 在当前目录创建名为 hellolib 的静态库,包含 hello.cpp 文件
add_library(hellolib STATIC hello.cpp)
# 将当前目录(包含 hellolib 的目录)添加到 hellolib 库的头文件搜索路径中
target_include_directories(hellolib PUBLIC .)
hellolib/hello.h
#pragma once
void hello();
hellolib/hello.cpp
#include <cstdio>
#include "hello.h"
void hello() {
printf("Hello, world!\n");
}
mathlib/CMakeLists.txt
add_library(mathlib SHARED math.cpp)
target_include_directories(mathlib PUBLIC .)
mathlib/math.h
#pragma once
int add(int a, int b);
int subtract(int a, int b);
mathlib/math.cpp
#include "math.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
main.cpp
#include <cstdio>
#include "hello.h"
#include "math.h"
int main() {
hello();
int result_add = add(5, 3);
int result_subtract = subtract(5, 3);
printf("5 + 3 = %d\n", result_add);
printf("5 - 3 = %d\n", result_subtract);
return 0;
}
8. CMake 与现代 C++ 开发
-
支持现代 C++ 标准的配置方法:
- 设置编译器标志以支持 C++11、C++14、C++17 等新标准。
- 示例:
set(CMAKE_CXX_STANDARD 17)
-
使用现代工具链和测试框架的集成:
- 配置和集成 Google Test、Catch2 等测试框架。
- 示例:
include(GoogleTest) add_executable(tests test1.cpp test2.cpp) target_link_libraries(tests PRIVATE GTest::GTest GTest::Main)
9. 其他一些细节
objdump -D
反汇编
- 功能说明:
objdump -D
是一个命令行工具,用于查看可执行文件或目标文件的反汇编内容。- 反汇编显示了机器码指令对应的汇编语言表示,帮助开发人员理解程序在底层的执行过程和优化代码。
ldd
- 功能说明:
ldd
命令用于打印可执行文件或共享库文件所依赖的动态链接库(共享对象)列表。- 在 Linux 系统上特别有用,可以检查程序运行时所需的共享库是否存在,并显示它们的路径。
静态库与动态库
-
静态库功能说明:
- 静态库(Static Library)是一种编译链接后形成的文件,包含了多个目标文件的归档文件(通常以
.a
或.lib
扩展名保存)。 - 静态库在链接阶段被整合到可执行文件中,使得可执行文件可以在没有其他依赖的情况下运行。
- 删除
.a
文件后,a.out
仍然可以运行
- 静态库(Static Library)是一种编译链接后形成的文件,包含了多个目标文件的归档文件(通常以
-
动态库功能说明:
- 动态库(Dynamic Library)是一种在运行时加载并链接的库文件,以共享对象(Shared Object)的形式存在(通常以
.so
扩展名保存)。 - 动态库可以在多个程序间共享,减少内存占用,但需要在运行时通过动态链接器加载到内存中。
- 删除
.a
文件后,a.out
不可以运行
- 动态库(Dynamic Library)是一种在运行时加载并链接的库文件,以共享对象(Shared Object)的形式存在(通常以
头文件的导入
- C/C++ 中的
#include
指令可以用来包含头文件。 - 双引号
"
形式的#include
:- 例如
#include "example.h"
。 - 预处理器会先在当前源文件所在的目录中查找
example.h
文件。 - 如果找不到,则会继续在包含该源文件的文件所在目录、项目指定的其他目录等地方查找。
- 例如
- 尖括号
<>
形式的#include
:- 例如
#include <example.h>
。 - 预处理器会直接在系统的标准头文件目录中(如
/usr/include
、/usr/local/include
等)查找example.h
文件。 - 这种形式通常用于包含标准库或者第三方库的头文件,因为这些头文件通常不在当前项目的源代码目录中。
- 例如
防止重复导入 #pragma once
- 功能说明:
#pragma once
是一种预处理器指令,用于防止同一个头文件被多次包含。- 替代传统的
#ifndef/#define
宏保护方式,更简洁且通常有更好的性能表现。
CMakeLists其他说明
# 除了头文件搜索目录以外,还有这些选项,PUBLIC 和 PRIVATE 对他们同理:
target_include_directories(myapp PUBLIC /usr/include/eigen3) # 添加头文件搜索目录
target_link_libraries(myapp PUBLIC hellolib) # 添加要链接的库
target_add_definitions(myapp PUBLIC MY_MACRO=1) # 添加一个宏定义
target_add_definitions(myapp PUBLIC -DMY_MACRO=1) # 与 MY_MACRO=1 等价
target_compile_options(myapp PUBLIC -fopenmp) # 添加编译器命令行选项
target_sources(myapp PUBLIC hello.cpp other.cpp) # 添加要编译的源文件
# 以及可以通过下列指令(不推荐使用),把选项加到所有接下来的目标去:
include_directories(/opt/cuda/include) # 添加头文件搜索目录
link_directories(/opt/cuda) # 添加库文件的搜索路径
add_definitions(MY_MACRO=1) # 添加一个宏定义
add_compile_options(-fopenmp) # 添加编译器命令行选项
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具