news 2026/4/18 12:36:14

【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/4/18 7:55:02

C#转java的最好利器easy-query就是efcore4j sqlsugar4j freesql4j

数据库模型image点击查看实体代码案例查询用户信息和最早开户的银行卡信息通过模型我们可以清晰的看到SysUser和BankCard是一对多的关系&#xff0c;eq如何实现这种一对多的数据返回呢&#xff1f;使用临时对象返回var list easyEntityQuery.queryable(SysUser.class).select(…

作者头像 李华
网站建设 2026/4/18 4:37:03

在线服务器的应用场景都有哪些?

在线服务器作为现代网络架构的核心组件&#xff0c;凭借其强大的计算、存储和数据处理能力&#xff0c;广泛应用于众多领域&#xff0c;深刻改变着人们的生活与工作方式。在线服务器用于对重要数据进行定期备份&#xff0c;防止数据丢失&#xff0c;备份数据可以存储在本地服务…

作者头像 李华
网站建设 2026/4/18 0:26:38

压电雨量监测站:物联网驱动的降雨监测

压电雨量监测站是一款基于物联网技术的现代雨量监测设备&#xff0c;由压电雨量传感器、采集器、太阳能供电系统及立杆支架等部分组成&#xff0c;该设备能够记录分钟级雨量、小时累计雨量、日累计雨量等不同时间维度的数据&#xff0c;并支持自定义日分界时间与降雨报警阈值。…

作者头像 李华
网站建设 2026/4/18 7:03:35

Hyrise终极内存OLAP引擎:为实时数据分析提供高速解决方案

Hyrise终极内存OLAP引擎&#xff1a;为实时数据分析提供高速解决方案 【免费下载链接】hyrise Hyrise is a research in-memory database. 项目地址: https://gitcode.com/gh_mirrors/hy/hyrise 还在为大数据查询性能瓶颈而苦恼吗&#xff1f;面对海量数据时&#xff0c…

作者头像 李华
网站建设 2026/4/18 8:07:00

揭秘阿里Qwen3-Next架构革命:800亿参数仅激活3B的效率突破之路

行业困境&#xff1a;大模型时代的效率瓶颈 【免费下载链接】Qwen3-Next-80B-A3B-Thinking Qwen3-Next-80B-A3B-Thinking 在复杂推理和强化学习任务中超越 30B–32B 同类模型&#xff0c;并在多项基准测试中优于 Gemini-2.5-Flash-Thinking 项目地址: https://ai.gitcode.com…

作者头像 李华