CMake指令: add_sub_directory以及工作流程

目录

1.简介

2.工作流程

3.示例场景

4.最佳实践

5.注意事项

6.总结

相关链接


1.简介

   add_subdirectory 是 CMake 中用于添加子目录参与构建的命令,允许将项目拆分为多个模块或子项目,实现代码的模块化管理。

        基本语法:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:子目录的源代码路径(相对于当前 CMakeLists.txt 的路径)。
  • binary_dir(可选):指定子目录的编译输出路径(默认与 source_dir 同级的 build 目录)。
  • EXCLUDE_FROM_ALL(可选):子目录不会被默认构建,需显式调用 add_subdirectory 或指定目标依赖。

        核心作用:

  1. 模块化构建:将项目拆分为多个子目录(如 srcteststhird_party),每个子目录包含独立的 CMakeLists.txt
  2. 依赖管理:子目录可定义库或可执行文件,供父目录或其他子目录链接。
  3. 递归构建:子目录中的 add_subdirectory 会被递归处理,实现多层级项目结构。

2.工作流程

1.目录解析

  • CMake 解析 add_subdirectory() 中的 source_dir 参数(如 src),确定子目录的路径。
  • 若指定 binary_dir(如 build/src),则将子目录的构建输出定向到该路径。
# 父目录 CMakeLists.txt
add_subdirectory(src)  # 无 binary_dir,输出到 build/src

2.变量传递:

  • 父→子传递:父目录中的变量(如 CMAKE_CXX_FLAGSPROJECT_NAME)自动传递给子目录。
  • 子→父传递:子目录可通过 set(... PARENT_SCOPE) 将变量回传给父目录。
# 父目录定义
set(COMMON_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAGS}")# 子目录自动继承 COMMON_FLAGS 和 CMAKE_CXX_FLAGS# 子目录
set(MY_VERSION "1.0.0" PARENT_SCOPE)  # 传递到父目录# 父目录
message("Version from subdir: ${MY_VERSION}")  # 输出 1.0.0

3.子目录处理

  • CMake 递归执行子目录中的 CMakeLists.txt 文件,生成目标(如 add_libraryadd_executable)。
  • 子目录中的 add_subdirectory() 会被递归处理,形成构建树。
# src/CMakeLists.txt
add_library(my_lib STATIC src/file.cpp)
target_include_directories(my_lib PUBLIC include)

4.依赖关系建立

  • 子目录中定义的目标(如 lib)可被父目录或其他子目录链接(如 target_link_libraries)。
  • CMake 自动处理目标间的依赖关系,确保正确的构建顺序。
# 父目录 CMakeLists.txt
add_subdirectory(src)  # 先处理子目录,生成 my_libadd_executable(main main.cpp)
target_link_libraries(main PRIVATE my_lib)  # 链接子目录的库

3.示例场景

假设你的项目结构如下:

MyProject/
├── CMakeLists.txt
├── src/
│   ├── CMakeLists.txt
│   ├── main.cpp
│   ├── utils.cpp
│   └── app.cpp
├── lib/
│   ├── CMakeLists.txt
│   ├── lib1.cpp
│   └── lib2.cpp
└── include/├── utils.h└── app.h

主目录的 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(MyProject)# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)# 添加包含目录
include_directories(${PROJECT_SOURCE_DIR}/include)# 添加子目录
add_subdirectory(lib)
add_subdirectory(src)# 定义最终的可执行文件,并链接子目录生成的库
add_executable(MyApp ${SRC_FILES})
target_link_libraries(MyApp PRIVATE mylib)# 输出配置信息
message(STATUS "Source files: ${SRC_FILES}")
message(STATUS "Library files: ${LIB_SOURCES}")

lib 子目录的 CMakeLists.txt

# lib/CMakeLists.txt# 定义库的源文件列表
set(LIB_SOURCESlib1.cpplib2.cpp
)# 创建静态库或动态库
add_library(mylib STATIC ${LIB_SOURCES})
# 或者创建动态库
# add_library(mylib SHARED ${LIB_SOURCES})# 指定库的包含目录
target_include_directories(mylib PUBLIC ${PROJECT_SOURCE_DIR}/include)# 将库源文件传递到父作用域
set(LIB_SOURCES ${LIB_SOURCES} PARENT_SCOPE)

src 子目录的 CMakeLists.txt

# src/CMakeLists.txt# 定义源文件列表,包含当前目录的源文件
set(SRC_FILESmain.cpputils.cppapp.cpp
)# 将源文件传递到父作用域
set(SRC_FILES ${SRC_FILES} PARENT_SCOPE)

4.最佳实践

1.项目结构建议

project/
├─ CMakeLists.txt          # 根目录:设置全局变量、添加子目录
├─ include/                # 公共头文件
├─ src/
│  ├─ CMakeLists.txt       # 定义库或可执行文件
│  └─ ...                  # 源代码
├─ tests/
│  ├─ CMakeLists.txt       # 测试相关目标
│  └─ ...                  # 测试代码
└─ third_party/            # 第三方依赖(可选)

2.模块化管理

在子目录中封装功能模块(如 add_library),通过 target_* 命令暴露接口,避免全局变量污染。

# 子目录 src/CMakeLists.txt
add_library(utils STATIC utils.cpp)
target_include_directories(utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)

3.条件编译与选项

使用 option 或 if 控制子目录是否参与构建,提升灵活性:

# 根据选项决定是否添加测试子目录
option(BUILD_TESTS "Build tests" ON)
if(BUILD_TESTS)add_subdirectory(tests)
endif()

4.排除默认构建(EXCLUDE_FROM_ALL)

# 子目录不会被默认构建,需显式依赖(如通过 add_dependencies)
add_subdirectory(third_party EXCLUDE_FROM_ALL)

5.注意事项

1.变量作用域问题

  • 子目录无法直接修改父目录的变量,需通过 PARENT_SCOPE 回传。
  • 避免在子目录中使用全局变量(如 include_directories),改用 target_* 命令。
set(VERSION "1.0" PARENT_SCOPE)  # 子目录回传变量到父目录

2.多级子目录

如果项目中有多级子目录,例如 src/module1 和 src/module2,可以在 src/CMakeLists.txt 中进一步使用 add_subdirectory(module1) 和 add_subdirectory(module2) 来递归处理这些子目录。

# src/CMakeLists.txtadd_subdirectory(module1)
add_subdirectory(module2)# 定义 src 目录下的源文件
set(SRC_FILESmain.cpputils.cppapp.cpp
)# 将源文件传递到父作用域
set(SRC_FILES ${SRC_FILES} PARENT_SCOPE)

3.构建顺序

  • add_subdirectory 的调用顺序决定子目录的处理顺序,但目标的构建顺序需通过 target_link_libraries 或 add_dependencies 显式指定。

4.使用相对路径和全局变量

        在子目录的 CMakeLists.txt 中,路径通常是相对于子目录本身的。例如,lib/CMakeLists.txt 中的 lib1.cpp 实际上指的是 lib/lib1.cpp。

        如果需要在多个子目录中共享变量或路径,可以在主目录中定义全局变量或使用 CMake 的全局范围选项(如 CACHE 变量)来传递信息。

5.错误处理

        如果 add_subdirectory() 指定的子目录不存在或没有 CMakeLists.txt 文件,CMake 会报错并中止配置过程。因此,确保所有子目录中都存在有效的 CMakeLists.txt 文件。

6.总结

     add_subdirectory()的优点:

  • 结构清晰:项目目录层次分明,便于导航和理解。
  • 模块化:每个模块或组件可以独立开发和测试。
  • 灵活性:每个子目录可以有不同的编译选项和依赖关系。
  • 可扩展性:轻松添加新的模块或组件,无需修改主 CMakeLists.txt

   add_subdirectory() 的核心价值在于实现项目的模块化构建,通过合理拆分代码和分层管理 CMakeLists.txt,可显著提升大型项目的可维护性。充分利用 CMake 提供的命令和功能,如 target_include_directories()target_link_libraries() 等,来管理依赖关系和编译选项。

相关链接

  • CMake 官网 CMake - Upgrade Your Software Build System
  • CMake 官方文档:CMake Tutorial — CMake 4.0.2 Documentation
  • CMake 源码:https://github.com/Kitware/CMake
  • CMake 源码:CMake · GitLab
  • 中文版基础介绍: CMake 入门实战 | HaHack
  • wiki: Home · Wiki · CMake / Community · GitLab

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84518.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++ 】智能指针:内存管理的 “自动导航仪”

目录 一、引入 二、智能指针的两大特性: 1、RAII 特点: 好处: 2、行为像指针 三、智能指针起初的缺陷:拷贝问题 四、几种智能指针的介绍。 1、C98出现的智能指针——auto_ptr auto_ptr解决上述拷贝构造的问题&#xff1a…

Java多线程实现之线程池详解

Java多线程实现之线程池详解 一、线程池的基本概念1.1 为什么需要线程池1.2 线程池的核心思想 二、Java线程池的实现2.1 Executor框架2.2 ThreadPoolExecutor构造参数 三、常见线程池类型3.1 FixedThreadPool3.2 CachedThreadPool3.3 SingleThreadExecutor3.4 ScheduledThreadP…

解码美元-黄金负相关:LSTM-Attention因果发现与黄金反弹推演

摘要:本文采用时间序列分析框架与自然语言处理(NLP)技术,对黄金与美元指数的负相关关系进行量化拆解。通过构建包含宏观经济因子、市场情绪指标及地缘风险的三维分析模型,揭示当前贵金属市场的核心驱动逻辑&#xff0c…

Asp.Net Core SignalR导入数据

文章目录 前言一、安装包二、使用步骤1.实现SignalR Hub服务:2.实现CSV文件解析及数据导入服务3.控制器4.前端实现(vue) 三、关键技术点说明总结 前言 导入CSV文件中的数据到数据库,使用CsvHelper解析CSV文件,SqlBulk…

Modern C++(四)声明

4、声明 声明是将名字引入到cpp程序中,不是每条声明都声明实际的东西。定义是足以使该名字所标识的实体被使用的声明。声明包含以下几种: 函数定义模板声明模板显式实例化模板显式特化命名空间定义链接说明属性声明(C11)空声明&…

目标检测yolo算法

yolov5s: 从github官网下载yolov5的算法之后,配置好环境(pycharm安装包-CSDN博客),再下载权重文件,比如默认的yolov5s.pt; 运行当前文件(detect.py),就能看…

一个超强的推理增强大模型,开源了,本地部署

大家好,我是 Ai 学习的老章 前几天介绍了MOE 模型先驱 Mistral 开源的代码 Agent 大模型——mistralai/Devstral-Small-2505 今天一起看看 Mistral 最新开源的推理大模型——Magistral Magistral 简介 Mistral 公司推出了首个推理模型 Magistral 及自研可扩展强…

MySQL体系架构解析(五):读懂MySQL日志文件是优化与故障排查的关键

MySQL文件 日志文件 在服务器运行过程中,会产生各种各样的日志,比如常规的查询日志,错误日志、二进制日志、 redo 日志和 Undo 日志等,日志文件记录了影响 MySQL 数据库的各种类型活动。 常见的日志文件有:错误日志…

湖南省网络建设与运维赛项竞赛规程及样题

湖南省职业院校技能竞赛样题 赛题说明 一、竞赛内容 “网络建设与运维”竞赛共分三个部分,其中: 第一部分:职业规范与素养 ( 5 分) 第二部分:网络搭建及安全部署项目 ( 50 分&#xff09…

华为云Flexus+DeepSeek征文 | 基于华为云ModelArts Studio搭建AnythingLLM聊天助手

华为云FlexusDeepSeek征文 | 基于华为云ModelArts Studio搭建AnythingLLM聊天助手 引言一、ModelArts Studio平台介绍华为云ModelArts Studio简介ModelArts Studio主要特点 二、AnythingLLM介绍AnythingLLM 简介AnythingLLM主要特点AnythingLLM地址 三、安装AnythingLLM应用下载…

板凳-------Mysql cookbook学习 (十--5)

6.11 计算年龄 2025年6月11日星期三 --创建表、初始化数据 drop table if exists sibling; create table sibling (name char(20),birth date );insert into sibling (name,birth) values(Gretchen,1942-04-14); insert into sibling (name,birth) values(Wilbur,1946-11-28)…

SAP RESTFUL接口方式发布SICF实现全路径

其他相关资料帖可参考: https://blog.csdn.net/woniu_maggie/article/details/146210752 https://blog.csdn.net/SAPmatinal/article/details/134349125 https://blog.csdn.net/weixin_44382089/article/details/128283417 【业务场景】 外部系统不想通过RFC (需…

在windows中安装或卸载nginx

首先在nginx的安装目录下cmd查看nginx的版本: 在看windows的服务中是否nginx注册为服务了 如果注册了服务就先将服务卸载了 在nginx的安装目录cmd执行命令 NginxService.exe uninstall “NginxService”是对应的注册的服务名称 关闭所有的相关nginx的服务这个也…

FaceFusion 技术深度剖析:核心算法与实现机制揭秘

在 AI 换脸技术蓬勃发展的浪潮中,FaceFusion 凭借其出色的换脸效果和便捷的操作,成为众多用户的首选工具。从短视频平台上的创意恶搞视频,到影视制作中的特效合成,FaceFusion 都展现出强大的实用性。而这一切的背后,是…

2. Web网络基础 - 协议端口

深入解析协议端口与netstat命令:网络工程师的实战指南 在网络通信中,协议端口是服务访问的门户。本文将全面解析端口概念,并通过netstat命令实战演示如何监控网络连接状态。 一、协议端口核心知识解析 1. 端口号的本质与分类 端口范围类型说…

嵌入式学习笔记 - freeRTOS vTaskPlaceOnEventList()函数解析

vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); 函数第一个参数为消息队列等待插入链表, void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) { configASSERT( pxEventList ); /…

Ubuntu 配置使用 zsh + 插件配置 + oh-my-zsh 美化过程

Ubuntu 配置使用 zsh 插件配置 oh-my-zsh 美化过程 引言zsh 安装及基础配置oh-my-zsh 安装及美化配置oh-my-zsh 安装主题美化配置主题自定义主题 插件安装及配置官方插件查看及启用插件安装 主题文件备份.zshrcre5et_self.zsh-theme 同步发布在个人笔记Ubuntu 配置使用 zsh …

Xilinx FPGA 重构Multiboot ICAPE2和ICAPE3使用

一、FPGA Multiboot 本文主要介绍基于IPROG命令的FPGA多版本重构,用ICAP原语实现在线多版本切换。需要了解MultiBoot Fallback点击链接。 如下图所示,ICAP原语可实现flash中n1各版本的动态切换,在工作过程中,可以通过IPROG命令切…

springMVC-11 中文乱码处理

前言 本文介绍了springMVC中文乱码的解决方案,同时也贴出了本人遇到过的其他乱码情况,可以根据自身情况选择合适的解决方案。 其他-jdbc、前端、后端、jsp乱码的解决 Tomcat导致的乱码解决 自定义中文乱码过滤器 老方法,通过javaW…

mysql-innoDB存储引擎事务的原理

InnoDB 存储引擎支持 ACID 事务,其事务机制是通过 Redo Log(重做日志)、Undo Log(回滚日志) 和 事务日志系统 来实现的。下面详细解析 InnoDB 事务的工作原理。 1.事务的基本特性(ACID) 特性描…