news 2026/2/27 6:17:09

【CMake 】CMake 中 add_executable 与 target_sources 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【CMake 】CMake 中 add_executable 与 target_sources 详解

CMake 中 add_executable 与 target_sources 详解

📖 前言

在 CMake 构建系统中,add_executabletarget_sources是两个用于管理源文件的重要命令。理解它们的关系、区别和使用场景,对于编写高质量的 CMake 配置文件至关重要。本文将深入探讨这两个命令的用法、关系和最佳实践。


1️⃣ add_executable:创建可执行文件目标

基本语法

add_executable(目标名 [源文件1] [源文件2] ...)

功能说明

add_executable用于创建一个可执行文件目标,这是构建可执行程序的第一步。它可以:

  • 创建目标:在 CMake 中注册一个可执行文件目标
  • 指定源文件:可以同时指定一个或多个源文件
  • 生成构建规则:告诉构建系统如何编译和链接这些源文件

基本示例

# 方式1:创建目标并指定所有源文件 add_executable(myapp main.cpp utils.cpp helper.cpp ) # 方式2:只创建目标,稍后添加源文件 add_executable(myapp main.cpp)

关键特点

  1. 必须首先调用:在添加源文件之前,必须先创建目标
  2. 每个可执行文件调用一次:一个add_executable对应一个可执行文件
  3. 源文件可选:可以在创建时指定源文件,也可以稍后用target_sources添加

2️⃣ target_sources:向目标添加源文件

基本语法

target_sources(目标名 PRIVATE|PUBLIC|INTERFACE [源文件...])

功能说明

target_sources用于向已存在的目标添加源文件。它可以:

  • 添加源文件:向已创建的目标添加更多源文件
  • 控制可见性:通过PRIVATEPUBLICINTERFACE控制源文件的可见性
  • 支持条件添加:可以根据条件动态添加不同的源文件

基本示例

# 先创建目标 add_executable(myapp main.cpp) # 然后添加更多源文件 target_sources(myapp PRIVATE utils.cpp helper.cpp utils.h # 可选:用于IDE显示和依赖跟踪 )

关键特点

  1. 目标必须已存在:必须先调用add_executableadd_library创建目标
  2. 可以多次调用:可以向同一个目标多次添加源文件
  3. 支持可见性控制:通过关键字控制源文件的传递性

3️⃣ 两者的关系和区别

核心关系

add_executable (创建目标) → target_sources (添加源文件) ↓ ↓ 必须首先调用 目标必须已存在

对比表格

特性add_executabletarget_sources
功能创建可执行文件目标向目标添加源文件
调用时机必须先调用必须在目标创建后调用
能否创建目标✅ 能❌ 不能
能否添加源文件✅ 能✅ 能
调用次数每个可执行文件一次可以多次调用
可见性控制❌ 不支持✅ 支持(PRIVATE/PUBLIC/INTERFACE)

使用方式对比

方式1:只用add_executable(适合简单项目)
# 一次性创建目标并指定所有源文件 add_executable(multifile main.cpp math_utils.cpp )

优点

  • 简单直接
  • 所有源文件一目了然
  • 适合源文件较少的项目

缺点

  • 不支持条件添加源文件
  • 不支持可见性控制
方式2:add_executable+target_sources(适合复杂项目)
# 先创建目标 add_executable(multifile main.cpp) # 然后添加更多源文件 target_sources(multifile PRIVATE math_utils.cpp math_utils.h )

优点

  • 支持条件添加源文件
  • 支持可见性控制
  • 更灵活,适合大型项目

缺点

  • 代码稍显复杂
  • 源文件分散在多处

4️⃣ 可见性关键字详解

target_sources支持三个可见性关键字,用于控制源文件的传递性:

PRIVATE(私有)

含义:源文件只用于构建当前目标,不会传递给依赖它的其他目标。

使用场景

  • 实现文件(.cpp
  • 仅内部使用的头文件
  • 可执行文件的所有源文件(因为可执行文件通常不被其他目标依赖)

示例

add_executable(myapp main.cpp) target_sources(myapp PRIVATE math_utils.cpp # 实现文件 math_utils.h # 头文件(仅内部使用) )

PUBLIC(公共)

含义:源文件用于构建当前目标,并且会传递给依赖它的其他目标。

使用场景

  • 库的公共头文件
  • 需要被使用该库的目标访问的头文件

示例

add_library(mylib STATIC math_lib.cpp) # PRIVATE:实现文件,不对外暴露 target_sources(mylib PRIVATE math_lib.cpp # 实现文件 ) # PUBLIC:公共头文件,使用者需要它 target_sources(mylib PUBLIC math_lib.h # 公共头文件,会传递给使用者 )

INTERFACE(接口)

含义:源文件不用于构建当前目标,但会传递给依赖它的其他目标。

使用场景

  • 接口库(INTERFACE library)的头文件
  • 仅提供头文件的库(header-only library)

示例

# 创建接口库(只有头文件,没有实现) add_library(header_only_lib INTERFACE) target_sources(header_only_lib INTERFACE header_only.h # 头文件,不编译,但使用者可以访问 )

可见性对比表

关键字用于构建当前目标传递给依赖者典型用途
PRIVATE✅ 是❌ 否实现文件、内部头文件
PUBLIC✅ 是✅ 是库的公共头文件
INTERFACE❌ 否✅ 是接口库的头文件

传递性演示

假设有:库A可执行文件B

# 库A add_library(A STATIC) target_sources(A PRIVATE file1.cpp # B 看不到 ) target_sources(A PUBLIC file2.h # B 可以看到 ) target_sources(A INTERFACE file3.h # B 可以看到(但A不编译它) ) # 可执行文件B add_executable(B main.cpp) target_link_libraries(B A)

结果

  • ✅ B 可以访问file2.h(PUBLIC)
  • ✅ B 可以访问file3.h(INTERFACE)
  • ❌ B 不能访问file1.cpp(PRIVATE)

5️⃣ 实际应用场景

场景1:简单项目(推荐用add_executable

cmake_minimum_required(VERSION 3.10) project(SimpleApp LANGUAGES CXX) # 所有源文件已知,一次性指定 add_executable(myapp main.cpp utils.cpp helper.cpp )

适用情况

  • 源文件数量少(< 10个)
  • 源文件固定,不需要条件添加
  • 项目结构简单

场景2:条件编译(推荐用target_sources

cmake_minimum_required(VERSION 3.10) project(ConditionalApp LANGUAGES CXX) # 先创建目标 add_executable(myapp main.cpp) # 根据平台添加不同的源文件 if(WIN32) target_sources(myapp PRIVATE windows_utils.cpp win_specific.cpp ) else() target_sources(myapp PRIVATE unix_utils.cpp unix_specific.cpp ) endif()

适用情况

  • 需要根据平台、配置等条件添加不同的源文件
  • 源文件较多,需要分类管理

场景3:库项目(混合使用)

cmake_minimum_required(VERSION 3.10) project(MyLibrary LANGUAGES CXX) # 创建静态库 add_library(mylib STATIC) # PRIVATE:实现文件,不对外暴露 target_sources(mylib PRIVATE math_lib.cpp # 实现文件 internal_helper.cpp # 内部辅助函数 internal_helper.h # 内部头文件 ) # PUBLIC:公共头文件,使用者需要它 target_sources(mylib PUBLIC math_lib.h # 公共头文件,会传递给使用者 ) # 创建可执行文件并链接库 add_executable(myapp main.cpp) target_link_libraries(myapp mylib)

适用情况

  • 创建库供其他目标使用
  • 需要区分公共接口和内部实现

场景4:包含头文件(IDE支持)

cmake_minimum_required(VERSION 3.10) project(IDEExample LANGUAGES CXX) # 创建目标 add_executable(myapp main.cpp) # 添加源文件 target_sources(myapp PRIVATE math_utils.cpp math_utils.h # 添加头文件,用于IDE显示和依赖跟踪 )

适用情况

  • 希望IDE(如 Visual Studio、CLion)在项目树中显示头文件
  • 需要更精确的依赖跟踪

6️⃣ 完整示例:多文件项目

让我们看一个完整的示例,演示两种方式的使用:

项目结构

02-多文件示例/ ├── CMakeLists.txt ├── main.cpp ├── math_utils.h └── math_utils.cpp

方式1:使用add_executable(当前方式)

cmake_minimum_required(VERSION 3.10) project(MultiFile LANGUAGES CXX) # 添加所有源文件 add_executable(multifile main.cpp math_utils.cpp )

方式2:使用add_executable+target_sources

cmake_minimum_required(VERSION 3.10) project(MultiFile LANGUAGES CXX) # 先创建目标 add_executable(multifile main.cpp) # 然后添加更多源文件 target_sources(multifile PRIVATE math_utils.cpp # 实现文件 math_utils.h # 头文件(可选,用于IDE显示) )

两种方式效果相同,选择哪种取决于项目需求和个人偏好。


7️⃣ 最佳实践

1. 何时使用add_executable

推荐使用

  • 简单项目,源文件数量少(< 10个)
  • 源文件固定,不需要条件添加
  • 希望所有源文件集中在一处,便于查看

2. 何时使用target_sources

推荐使用

  • 需要根据条件添加不同的源文件
  • 源文件较多,需要分类管理
  • 创建库,需要区分公共接口和内部实现
  • 希望IDE显示头文件

3. 可见性关键字选择

  • 可执行文件:几乎总是使用PRIVATE
  • 库的实现文件:使用PRIVATE
  • 库的公共头文件:使用PUBLIC
  • 接口库的头文件:使用INTERFACE

4. 头文件的处理

技术角度

  • 头文件(.h)不需要在 CMake 中显式列出
  • 编译器会自动通过#include处理头文件

实践角度

  • 可以列出头文件用于IDE显示和依赖跟踪
  • 对于库,公共头文件应该用PUBLIC列出

5. 代码组织建议

# 推荐:清晰的注释和分组 cmake_minimum_required(VERSION 3.10) project(MyProject LANGUAGES CXX) # ========== 创建可执行文件 ========== add_executable(myapp main.cpp) # ========== 添加源文件 ========== target_sources(myapp PRIVATE utils.cpp helper.cpp utils.h helper.h )

8️⃣ 常见问题

Q1: 头文件需要添加到 CMakeLists.txt 吗?

A: 技术上不需要,编译器会自动处理#include。但可以添加用于:

  • IDE 项目树显示
  • 依赖跟踪(头文件改变时触发重新编译)

Q2: 可执行文件可以用 PUBLIC 或 INTERFACE 吗?

A: 技术上可以,但通常没有必要。因为可执行文件不会被其他目标依赖,所以PRIVATE就足够了。

Q3:add_executabletarget_sources可以混用吗?

A: 可以!可以先在add_executable中指定一些源文件,然后用target_sources添加更多。

add_executable(myapp main.cpp utils.cpp) target_sources(myapp PRIVATE helper.cpp)

Q4: 什么时候必须用target_sources

A: 以下情况必须使用:

  • 需要条件添加源文件
  • 需要控制源文件的可见性(PRIVATE/PUBLIC/INTERFACE)
  • 在子目录的 CMakeLists.txt 中向父目录的目标添加源文件

9️⃣ 总结

核心要点

  1. add_executable:创建可执行文件目标,可以同时指定源文件
  2. target_sources:向已存在的目标添加源文件,支持可见性控制
  3. 关系add_executable创建目标,target_sources添加源文件
  4. 可见性PRIVATE(私有)、PUBLIC(公共)、INTERFACE(接口)

选择建议

  • 简单项目:使用add_executable一次性指定所有源文件
  • 复杂项目:使用add_executable+target_sources,更灵活
  • 库项目:使用target_sources区分公共接口和内部实现

记忆口诀

  • add_executable= 创建目标
  • target_sources= 添加文件
  • PRIVATE= 私有,自己用
  • PUBLIC= 公共,大家用
  • INTERFACE= 接口,给别人用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/14 19:43:51

2003-2021近全球农业气候干旱监测数据集(0.25度/逐月)

2003-2021近全球月度农业气候干旱监测数据集 一、数据介绍 全球干旱发生频率不断上升且严重程度日益加剧&#xff0c;这给农业和气候资源管理带来了严峻挑战&#xff0c;凸显了对全面干旱数据的迫切需求。为应对这一问题&#xff0c;我们推出了近全球农业气候干旱监测数据集。…

作者头像 李华
网站建设 2026/2/9 9:19:52

接口防抖问答整理(拳打面试官)

接口防抖&#xff08;Spring AOPRedis&#xff09;核心问答&#xff08;面试/复习重点&#xff09; 一、核心亮点类问题 Q1&#xff1a;这套接口防抖方案最核心的设计亮点是什么&#xff1f;解决了什么问题&#xff1f; A1&#xff1a; 核心亮点是「注解驱动Redis原子锁降级兼容…

作者头像 李华
网站建设 2026/2/25 6:42:08

RFID+机械臂:工业零部件加工后智能分拣

RFID机械臂&#xff1a;工业零部件加工后智能分拣在某精密机械加工厂的零部件后处理车间&#xff0c;一台六轴机械臂正精准抓取不同规格的加工件&#xff0c;其控制柜旁嵌入的超高频RFID读写器&#xff0c;正有条不紊的读取工件工装托盘上的标签信息。这一“RFID精准识别机械臂…

作者头像 李华
网站建设 2026/2/22 16:17:18

AIReview 实战:用 AI 把代码评审提质提速

们要解决什么问题&#xff1f;评审效率低&#xff1a;PR 大、改动多&#xff0c;人工通读耗时长且容易遗漏风险。质量难对齐&#xff1a;不同评审人标准不一&#xff0c;建议分散在聊天和评论里&#xff0c;缺少沉淀与复用。反馈不成体系&#xff1a;只见“问题”&#xff0c;不…

作者头像 李华
网站建设 2026/2/26 17:14:06

MindSpore 高阶实战:从手写训练步到自动混合精度加速

在昇腾&#xff08;Ascend&#xff09;计算产业生态中&#xff0c;MindSpore 作为原生 AI 框架&#xff0c;其最大的魅力在于动静统一与函数式编程的设计理念。对于习惯了 PyTorch 面向对象式训练循环&#xff08;Forward -> Backward -> Optimizer Step&#xff09;的开…

作者头像 李华
网站建设 2026/2/16 9:25:18

无尘模组000

一、产品现状与核心优势 产品矩阵&#xff1a;TOYO 无尘模组涵盖三大核心系列&#xff0c;均已实现标准化量产&#xff1a;GCH 系列&#xff1a;轨道内嵌式丝杆模组&#xff0c;外接真空源可实现 CLASS 1 洁净度&#xff0c;半导体设备首选TOYO东佑达 ECB 系列&#xff1a;无尘…

作者头像 李华