文章目录
- [一、目标的默认输出路径 以及 如何修改输出路径](#一、目标的默认输出路径 以及 如何修改输出路径)
-
- [1. 源文件 和 头文件中内容(含完整项目路径展示)](#1. 源文件 和 头文件中内容(含完整项目路径展示))
- [2. 顶层 以及 子目录下CMakeList.txt文件 的编辑](#2. 顶层 以及 子目录下CMakeList.txt文件 的编辑)
- [3. 目标(可执行程序 、动/静态库)的默认输出路径](#3. 目标(可执行程序 、动/静态库)的默认输出路径)
- [4. 如何修改 目标(可执行程序 、动/静态库)的输出路径](#4. 如何修改 目标(可执行程序 、动/静态库)的输出路径)
- 二、cmake实现属性传递的秘密
-
- [1. 属性的传递机制](#1. 属性的传递机制)
- [2. 多层级CMakeLists.txt文件,中间层PUBLIC继承 传递属性的效果](#2. 多层级CMakeLists.txt文件,中间层PUBLIC继承 传递属性的效果)
- [3. 多层级CMakeLists.txt文件,中间层PRIVATE继承 中断属性传递的效果](#3. 多层级CMakeLists.txt文件,中间层PRIVATE继承 中断属性传递的效果)
一、目标的默认输出路径 以及 如何修改输出路径
1. 源文件 和 头文件中内容(含完整项目路径展示)
项目路径展示图:
bash
my_math
├── build
├── CMakeLists.txt
├── my_app
│ ├── CMakeLists.txt
│ └── main.cpp
└── my_lib
├── CMakeLists.txt
├── include
│ └── math.h
└── src
├── add.cpp
└── sub.cpp
- my_math/my_lib/src/add.cpp

-
my_math/my_lib/src/sub.cpp

-
my_math/my_lib/include/math.h

-
my_math/my_app/main.cpp
编辑以下源文件会报错,因为该源文件包含的 main.h头文件是用户自定义的,该头文件没放在系统路径下,系统查找不到该源文件,于是报错。
需要手动添加一下系统的头文件查找路径,以下是添加步骤:




${workspaceFolder} 表示项目目录的路径:也就是 /home/zh/CMake_test


2. 顶层 以及 子目录下CMakeList.txt文件 的编辑
- my_math/my_lib/CMakeLists.txt

- my_math/my_app/CMakeLists.txt

- my_math/CMakeLists.txt
子目录my_app的CMakeLists.txt文件中定义的 目标main 依赖 子目录my_lib的CMakeLists.txt文件中定义的 目标MyMath
所以 顶层CMakeLists.txt下,一定要先add_subdirectory 子目录my_lib,再add_subdirectory 子目录my_app

3. 目标(可执行程序 、动/静态库)的默认输出路径
bash
my_math
├── build(空目录)
├── CMakeLists.txt
├── my_app
│ ├── CMakeLists.txt
│ └── main.cpp
└── my_lib
├── CMakeLists.txt
├── include
│ └── math.h
└── src
├── add.cpp
└── sub.cpp


目标(可执行程序 、动/静态库)的默认输出路径:
CMake默认会将目标的输出文件(可执行文件、动态库、静态库)放置在构建目录(${CMAKE_BINARY_DIR})中,且该路径与定义该目标的CMakeLists.txt文件相对于项目根目录的路径保持一致。
4. 如何修改 目标(可执行程序 、动/静态库)的输出路径
| 目标类型 | 属性名(单个目标) | 全局变量名 | 适用场景 |
|---|---|---|---|
| 可执行文件 | RUNTIME_OUTPUT_DIRECTORY | CMAKE_RUNTIME_OUTPUT_DIRECTORY | 控制可执行文件(如 .exe)的输出路径 |
| 动态库 | LIBRARY_OUTPUT_DIRECTORY | CMAKE_LIBRARY_OUTPUT_DIRECTORY | 控制动态库文件(如 .so、.dll)的输出路径 |
| 静态库 | ARCHIVE_OUTPUT_DIRECTORY | CMAKE_ARCHIVE_OUTPUT_DIRECTORY | 控制静态库文件(如 .a、.lib)的输出路径 |
- my_math/my_app/CMakeLists.txt
RUNTIME_OUTPUT_DIRECTORY:可执行程序目标 的输出路径
通过 set_target_properties函数 可以修改目标的属性

- my_math/my_lib/CMakeLists.txt
ARCHIVE_OUTPUT_DIRECTORY:静态库目标 的输出路径
通过 set_target_properties函数 可以修改目标的属性


二、cmake实现属性传递的秘密
1. 属性的传递机制
| 关键字 | 对当前目标的影响 | 是否传播 | 对当前目标依赖者的影响 | 解释 |
|---|---|---|---|---|
| PRIVATE | ✅ 生效 | ❌ 否 | ❌ 不生效 | 只自己用 |
| PUBLIC | ✅ 生效 | ✅ 是 | ✅ 生效 | 自己-依赖者用 |
| INTERFACE | ❌ 不生效 | ✅ 是 | ✅ 生效 | 自己不用-依赖者用 |
2. 多层级CMakeLists.txt文件,中间层PUBLIC继承 传递属性的效果
项目路径展示图:
bash
Project/
├── CMakeLists.txt # 顶层
├── core/ # 第1层
│ ├── CMakeLists.txt
│ ├── include/
│ │ └── core_utils.h
│ └── src/
│ └── core_utils.cpp
├── network/ # 第2层
│ ├── CMakeLists.txt
│ ├── include/
│ │ └── net_client.h
│ └── src/
│ └── net_client.cpp
└── app/ # 第3层
├── CMakeLists.txt
├── include/
│ └── app_config.h
└── src/
└── main.cpp
- 顶层 CMakeLists.txt (Project/CMakeLists.txt)
bash
cmake_minimum_required(VERSION 3.15)
project(PropertyPropagationDemo)
# 添加第一层目录
add_subdirectory(core)
- 第一层:核心库 (core/CMakeLists.txt)
bash
# 创建静态库目标
add_library(core_lib STATIC
src/core_utils.cpp
)
# PUBLIC 属性:自身使用 + 传递给使用者
target_include_directories(core_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include # 头文件目录
)
target_compile_definitions(core_lib PUBLIC
CORE_ENABLE_LOGGING=1 # 日志功能开关
)
# PRIVATE 属性:仅自身使用
target_compile_definitions(core_lib PRIVATE
CORE_DEBUG_MODE=0 # 内部调试标志
)
# INTERFACE 属性:仅传递给使用者
target_compile_definitions(core_lib INTERFACE
USE_CORE_API=1 # 标识使用核心API
)
# 添加第二层目录
add_subdirectory(../network ${CMAKE_BINARY_DIR}/network)
- 第二层:网络库 (network/CMakeLists.txt)
bash
# 创建共享库目标
add_library(network_lib SHARED
src/net_client.cpp
)
# PUBLIC 继承 core_lib 的属性
target_link_libraries(network_lib PUBLIC
core_lib
)
# 添加本层特有的 PUBLIC 属性
target_include_directories(network_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_compile_definitions(network_lib PUBLIC
NETWORK_TIMEOUT=3000 # 网络超时设置
)
# PRIVATE 属性(仅本层使用)
target_compile_options(network_lib PRIVATE
-Wno-deprecated # 禁用弃用警告
)
# INTERFACE 属性(仅传递给使用者)
target_compile_definitions(network_lib INTERFACE
USE_NETWORK_API=1 # 标识使用网络API
)
# 添加第三层目录
add_subdirectory(../app ${CMAKE_BINARY_DIR}/app)
- 第三层:应用程序 (app/CMakeLists.txt)
bash
# 创建可执行文件
add_executable(my_app
src/main.cpp
)
# PRIVATE 链接 network_lib
target_link_libraries(my_app PRIVATE
network_lib
)
# 添加本层属性
target_compile_definitions(my_app PRIVATE
APP_VERSION="1.0"
)
# 打印最终继承的属性
message("\n 最终应用程序继承的属性:")
message("----------------------------------------")
# 获取包含目录
get_target_property(app_includes my_app INCLUDE_DIRECTORIES)
message("包含目录: ${app_includes}")
# 获取编译定义
get_target_property(app_defines my_app COMPILE_DEFINITIONS)
message("编译定义: ${app_defines}")
# 获取编译选项
get_target_property(app_options my_app COMPILE_OPTIONS)
message("编译选项: ${app_options}")
- 执行 CMake 配置后输出(属性继承结果分析):
bash
最终应用程序继承的属性:
----------------------------------------
包含目录:
/Project/core/include # ← 来自 core_lib (PUBLIC)
/Project/network/include # ← 来自 network_lib (PUBLIC)
编译定义:
CORE_ENABLE_LOGGING=1 # ← 来自 core_lib (PUBLIC)
USE_CORE_API=1 # ← 来自 core_lib (INTERFACE)
NETWORK_TIMEOUT=3000 # ← 来自 network_lib (PUBLIC)
USE_NETWORK_API=1 # ← 来自 network_lib (INTERFACE)
APP_VERSION="1.0" # ← 自身定义 (PRIVATE)
编译选项:
(空) # ← 未继承任何编译选项
- 属性传递路径详解
| 属性来源 | 属性类型 | 属性内容 | 是否传递到 my_app | 原因说明 |
|---|---|---|---|---|
| core_lib | PUBLIC | CORE_ENABLE_LOGGING=1 | ✅ | PUBLIC 属性通过 network_lib 传递 |
| -- | PUBLIC | include/core | ✅ | PUBLIC 头文件目录被继承 |
| -- | PRIVATE | CORE_DEBUG_MODE=0 | ❌ | PRIVATE 属性不传递 |
| -- | INTERFACE | USE_CORE_API=1 | ✅ | INTERFACE 属性被继承 |
| network_lib | PUBLIC | NETWORK_TIMEOUT=3000 | ✅ | 直接 PUBLIC 属性 |
| -- | PUBLIC | include/network | ✅ | PUBLIC 头文件目录被继承 |
| -- | PRIVATE | -Wno-deprecated | ❌ | PRIVATE 编译选项不传递 |
| -- | INTERFACE | USE_NETWORK_API=1 | ✅ | INTERFACE 属性被继承 |
| my_app | PRIVATE | APP_VERSION="1.0" | ✅ | 自身属性 |
- 关键继承规则图解(属性如何传递到 目标my_app)
bash
core_lib
├── PUBLIC: → network_lib → my_app
├── PRIVATE: × 停止传递
└── INTERFACE: → network_lib → my_app # network_lib是 PUBLIC继承core_lib,
# 所以 network继承 core_lib传递过来的属性后,
network_lib # 可以将 继承到的属性继续向 my_app传递
├── PUBLIC: → my_app
├── PRIVATE: × 停止传递
└── INTERFACE: → my_app
my_app
└── PRIVATE: 自身属性
3. 多层级CMakeLists.txt文件,中间层PRIVATE继承 中断属性传递的效果
修改后的第二层 CMakeLists.txt (network/CMakeLists.txt)
- 原:network_lib目标 PUBLIC继承 core_lib
target_link_libraries(network_lib PUBLIC core_lib)- 修改为:network_lib目标 PRIVATE继承 core_lib
target_link_libraries(network_lib PRIVATE core_lib)
bash
# ... 其他部分保持不变 ...
# 由 PUBLIC继承core_lib 改为 PRIVATE继承core_lib
target_link_libraries(network_lib PRIVATE # 关键修改
core_lib
)
# ... 其他部分保持不变 ...
- 执行 CMake 配置后输出(属性继承结果分析):
bash
最终应用程序继承的属性:
----------------------------------------
包含目录:
/Project/network/include # ← 仅来自 network_lib (PUBLIC)
编译定义:
NETWORK_TIMEOUT=3000 # ← 来自 network_lib (PUBLIC)
USE_NETWORK_API=1 # ← 来自 network_lib (INTERFACE)
APP_VERSION="1.0" # ← 自身定义 (PRIVATE)
编译选项:
(空)
- 属性传递路径详解
| 属性来源 | 属性类型 | 属性内容 | 是否传递到 my_app | 原因说明 |
|---|---|---|---|---|
| core_lib | PUBLIC | CORE_ENABLE_LOGGING=1 | ❌ | network_lib目标 PRIVATE 继承core_lib 中断传递 |
| -- | PUBLIC | include/core | ❌ | network_lib目标 PRIVATE 继承core_lib 中断传递 |
| -- | PRIVATE | CORE_DEBUG_MODE=0 | ❌ | PRIVATE 属性不传递 |
| -- | INTERFACE | USE_CORE_API=1 | ❌ | network_lib目标 PRIVATE 继承core_lib 中断传递 |
| network_lib | PUBLIC | NETWORK_TIMEOUT=3000 | ✅ | 直接 PUBLIC 属性 |
| -- | PUBLIC | include/network | ✅ | PUBLIC 头文件目录被继承 |
| -- | PRIVATE | -Wno-deprecated | ❌ | PRIVATE 编译选项不传递 |
| -- | INTERFACE | USE_NETWORK_API=1 | ✅ | INTERFACE 属性被继承 |
| my_app | PRIVATE | APP_VERSION="1.0" | ✅ | 自身属性 |
- 关键继承规则图解(属性如何传递到 目标my_app)
bash
core_lib
├── PUBLIC: → network_lib (但停止传递)
├── PRIVATE: × 停止传递
└── INTERFACE: → network_lib (但停止传递) # network_lib是 PRIVATE继承core_lib,
# 所以 network继承到 core_lib传递过来的属性后,
network_lib # 不能将 继承到的属性继续向 my_app传递
├── PUBLIC: → my_app
├── PRIVATE: × 停止传递
└── INTERFACE: → my_app
my_app
└── PRIVATE: 自身属性
