一、构建目标:从零开始理解 CMake 的核心
说实话,我刚接触 CMake 那会儿,最困惑的就是「构建目标」这个概念。你想想看,我们写代码无非就是编译成可执行文件或者库文件,为什么 CMake 非要搞出个「目标」来?
后来在项目里踩过几次坑才明白——构建目标就是 CMake 世界里的一切基础。它不只是一个文件,而是一个完整的构建单元,包含了源文件、编译选项、依赖关系、链接库等等信息。
1.1 add_executable:生成可执行文件
这是最常用的命令,没有之一。它的作用就是告诉 CMake:「嘿,我要生成一个可执行文件」。
add_executable(my_app main.cpp utils.cpp)
上面这行代码,创建了一个名为 my_app 的构建目标。CMake 会编译 main.cpp 和 utils.cpp,然后链接成可执行文件。
my app 带空格的,结果在 Windows 上折腾了半天路径问题。
如果你有多个源文件,可以这样写:
add_executable(my_app
main.cpp
utils.cpp
network.cpp
database.cpp
)
我个人喜欢把每个源文件单独一行,这样 git diff 看得清楚。你想想看,要是把所有文件写在一行,别人改代码时都不知道你加了哪个文件。
1.2 add_library:生成库文件
库文件分三种:静态库、动态库、模块库。我一个个说。
| 类型 | 关键字 | 生成文件 | 使用场景 |
|---|---|---|---|
| 静态库 | STATIC | .a / .lib | 代码直接打进可执行文件 |
| 动态库 | SHARED | .so / .dll | 运行时加载,节省空间 |
| 模块库 | MODULE | .so / .dll | 插件系统,动态加载 |
add_library(my_lib STATIC math.cpp io.cpp)
add_library(my_shared SHARED network.cpp protocol.cpp)
add_library(my_plugin MODULE plugin_core.cpp)
__declspec(dllexport)。
1.3 目标属性:给目标加配置
光有目标还不够,你得告诉 CMake 怎么编译它。目标属性就是干这个的。
add_executable(my_app main.cpp)
# 设置 C++ 标准
set_target_properties(my_app PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
)
# 设置输出目录
set_target_properties(my_app PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
# 设置编译定义
target_compile_definitions(my_app PRIVATE
USE_OPENSSL
ENABLE_DEBUG=1
)
常用的目标属性我列几个:
CXX_STANDARD:C++ 标准版本POSITION_INDEPENDENT_CODE:位置无关代码,动态库必须开LINKER_LANGUAGE:指定链接器语言FOLDER:在 IDE 里分组显示
add_definitions 和 include_directories 了,那会让你的构建系统变得难以维护。
1.4 目标依赖关系:谁依赖谁
项目大了,目标之间肯定有依赖。比如可执行文件依赖静态库,静态库又依赖另一个库。
add_library(core STATIC core.cpp)
add_library(utils STATIC utils.cpp)
add_executable(my_app main.cpp)
# 链接库
target_link_libraries(my_app PRIVATE core utils)
# 传递头文件路径
target_include_directories(core PUBLIC include)
target_include_directories(utils PRIVATE include/internal)
这里有个关键概念——传递性。PUBLIC 的意思是:我自己的使用者也能看到这个头文件路径。PRIVATE 的意思是:只有我自己能看到。
举个例子:
core库的include目录是 PUBLIC,所以my_app也能用utils库的include/internal是 PRIVATE,my_app看不到
知识体系总览
下面这张图,把本章的核心内容串起来了:
总结一下
构建目标这个概念,说白了就是 CMake 帮你管理的一个「编译任务」。它知道要编译哪些源文件、用什么编译选项、链接哪些库、输出到哪里。
我个人觉得,理解构建目标是学好 CMake 的第一步。你想想看,后面讲的自定义命令、生成器表达式、安装规则,全都是围绕着目标来展开的。
嗯,这一章的内容就到这里。记住三个核心命令:add_executable、add_library、target_link_libraries,再加上目标属性的概念,你就能应付大部分项目了。