1.如何使用CMake构建大型项目-c++ linux编程:从0实现muduo库系列
章节规划
MyMuduo是一个基于C++11的多线程网络库,参考陈硕的muduo实现。该库采用Reactor模式,以事件驱动和非阻塞I/O为核心,提供了高性能的网络,章节规划如下所示:
第一部分:基础设施
1.项目概述与环境搭建
- muduo库整体架构介绍
- 编译环境配置
- CMake构建系统
- 实现:CMakeLists.txt
2.基础类型与工具类
- C++11特性介绍
- 实现:Types.h
- 实现:copyable.h和noncopyable.h
- 实现:StringPiece.h
3.时间类的实现
- 时间戳处理
- 实现:Timestamp.h/cc
- 实现:Date.h/cc
- 实现:TimeZone.h/cc
4.异常处理机制
- C++异常处理
- 实现:Exception.h/cc
- 堆栈跟踪的实现
5.日志系统基础
- 实现:LogStream.h/cc
- 实现:Logging.h/cc
- 格式化输出
6.原子操作与互斥量
- 实现:Atomic.h
- 实现:Mutex.h
- 实现:Condition.h/cc
7.线程安全的单例模式
- 实现:Singleton.h
- 实现:ThreadLocal.h
- 实现:ThreadLocalSingleton.h
第二部分:线程库
1.线程基础设施
- 实现:CurrentThread.h/cc
- 线程标识符处理
2.线程类的实现
- 实现:Thread.h/cc
- RAII线程管理
3.阻塞队列的实现
- 实现:BlockingQueue.h
- 实现:BoundedBlockingQueue.h
4.日志系统进阶
- 实现:AsyncLogging.h/cc
- 实现:LogFile.h/cc
- 实现:FileUtil.h/cc
- 实现:Processinfo.h/cc
5.线程池的实现
- 实现:ThreadPool.h/cc
- 线程池调度策略
6.弱回调机制
- 实现:WeakCallback.h
- 解决循环引用问题
第三部分:网络库基础
1.网络地址封装
- 实现:InetAddress.h/cc
- IPv4/IPv6支持
2.Socket API封装(上)
- 实现:SocketsOps.h/cc
- 错误处理
3.Socket API封装(下)
- 实现:Socket.h/cc
- 套接字基础操作
4.Buffer设计与实现
- 实现:Buffer.h/cc
- 缓冲区管理
5.Channel和Poller设计与实现
- 实现:Channel.h/cc
- 实现:Poller.h/cc
- 实现:poller/PollPoller.h/cc
6.EPollPoller设计与实现
- 实现:poller/EPollPoller.h/cc
7.定时器实现
- 实现:Timer.h/cc
- 实现:TimerId.h
- 实现:TimerQueue.h/cc
- 定时任务管理
第四部分:网络库核心
1.接受器实现
- 实现:Acceptor.h/cc
- 新连接处理
2.连接器实现
- 实现:Connector.h/cc
- 客户端连接
3.事件循环线程
- 实现:EventLoopThread.h/cc
4.事件循环线程池
- 实现:EventLoopThreadPool.h/cc
- 多IO线程架构
5.TCP连接的实现
- 实现:TcpConnection.h/cc
- 连接状态管理
6.TCP服务器的实现
- 实现:TcpServer.h/cc
- 服务器框架
7.TCP客户端的实现
- 实现:TcpClient.h/cc
- 客户端框架
8.事件循环Reactor
- 测试 单epoll模型
- 测试单epoll +线程池模型
- 测试main reactor + sub reactor模型
- 测试main reactor + sub reactor模型 + 线程池模型
1.本节重点
掌握如何构建大工程即可,CMakeLists.txt的很多细节可以后续有修改时再深入研究。
重点:
视频讲解(源码领取见视频):《C++Linux编程进阶:从0实现muduo》-第1讲.CMake构建大型项目
2.lesson1构建框架
每节课对应一个目录,比如lesson1/lesson2....,每章节新增内容基于前一章节的代码。
创建项目的基本结构。 mkdir -p mymuduo/lesson1
现在让我们创建项目的基本目录结构:
cd mymuduo/lesson1 mkdir -p base net/poller net/http examples
lesson1/ ├── CMakeLists.txt # 主CMakeLists.txt文件 ├── README.md # 项目说明文件 ├── base/ # 基础库目录 │ ├── CMakeLists.txt # base目录的CMakeLists.txt │ └── Version.h # 版本信息头文件 ├── net/ # 网络库目录 │ ├── CMakeLists.txt # net目录的CMakeLists.txt │ ├── http/ # HTTP相关代码目录 │ └── poller/ # 轮询器相关代码目录 ├── examples/ # 示例代码目录 │ ├── CMakeLists.txt # examples目录的CMakeLists.txt │ └── hello-muduo.cc # hello-muduo示例程序 ├── build/ # 构建输出目录
本质是通过add_subdirectory(子目录) 命令添加子目录的编译。
2.1 CMakeLists.txt指令说明
2.1.1 主要CMake指令说明
- cmake_minimum_required(VERSION 3.10) - 指定CMake的最低版本要求
- project(mymuduo C CXX) - 定义项目名称和使用的语言
- set(CMAKE_CXX_STANDARD 17) - 设置C++标准为C++17
- set(CMAKE_BUILD_TYPE "Release") - 设置构建类型为Release
- set(CXX_FLAGS ...) - 设置C++编译选项
- string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") - 将列表转换为字符串
- set(EXECUTABLE_OUTPUT_PATH ...) - 设置可执行文件输出路径
- set(LIBRARY_OUTPUT_PATH ...) - 设置库文件输出路径
- include_directories(${PROJECT_SOURCE_DIR}) - 添加包含目录
- add_subdirectory(base) - 添加子目录base
- add_subdirectory(net) - 添加子目录net
- option(BUILD_EXAMPLES "Build examples" ON) - 定义是否构建示例的选项
- add_subdirectory(examples) - 添加示例子目录
- message(STATUS ...) - 打印状态信息
2.1.2 子目录CMakeLists.txt中的指令
base/CMakeLists.txt
- set(base_SRCS ...) - 设置base库的源文件列表(暂时注释)
- add_library(mymuduo_base ${base_SRCS}) - 添加库目标(暂时注释)
- target_link_libraries(mymuduo_base pthread rt) - 链接库(暂时注释)
- install(TARGETS mymuduo_base DESTINATION lib) - 安装目标(暂时注释)
- file(GLOB HEADERS "*.h") - 收集所有头文件
- install(FILES ${HEADERS} DESTINATION include/mymuduo/base) - 安装头文件
net/CMakeLists.txt
- set(net_SRCS ...) - 设置net库的源文件列表(暂时注释)
- add_library(mymuduo_net ${net_SRCS}) - 添加库目标(暂时注释)
- target_link_libraries(mymuduo_net mymuduo_base pthread) - 链接库(暂时注释)
- install(TARGETS mymuduo_net DESTINATION lib) - 安装目标(暂时注释)
- file(GLOB HEADERS "*.h") - 收集所有头文件
- install(FILES ${HEADERS} DESTINATION include/mymuduo/net) - 安装头文件
- add_subdirectory(http) - 添加http子目录
- add_subdirectory(poller) - 添加poller子目录
examples/CMakeLists.txt
- if(NOT CMAKE_BUILD_NO_EXAMPLES) - 条件判断是否构建示例
- add_executable(hello-muduo hello-muduo.cc) - 添加可执行文件目标
2.1.3 编译参数说明
让我为您解释这三个 CMake 变量的作用和关系:
1.CMAKE_CXX_FLAGS:
- 这是基础的 C++ 编译选项
- 这些选项会应用到所有的构建类型(Debug、Release 等)
- 通常包含一些通用的编译选项,比如警告级别、架构设置等
- 在我们的例子中,包含了 -Wall、-Wextra 等通用选项
2.CMAKE_CXX_FLAGS_DEBUG:
- 这是 Debug 模式特有的编译选项
- 只在 Debug 构建时使用
- 通常包含调试相关的选项,如 -g(生成调试信息)
- 在我们的例子中,设置了 -g -O0 -DDEBUG
- 这些选项会与 CMAKE_CXX_FLAGS 中的选项合并
3.CMAKE_CXX_FLAGS_RELEASE:
- 这是 Release 模式特有的编译选项
- 只在 Release 构建时使用
- 通常包含优化相关的选项,如 -O2(优化级别)
- 在我们的例子中,设置了 -O2 -DNDEBUG
- 这些选项也会与 CMAKE_CXX_FLAGS 中的选项合并
它们的关系是:
- 当构建 Debug 版本时,最终的编译选项是:CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG
- 当构建 Release 版本时,最终的编译选项是:CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_RELEASE
例如,在我们的配置中:
- Debug 模式下的完整编译选项会是:
-Wall -Wextra -Werror ... (来自CMAKE_CXX_FLAGS) -g -O0 -DDEBUG (来自CMAKE_CXX_FLAGS_DEBUG)
- Release 模式下的完整编译选项会是:
-Wall -Wextra -Werror ... (来自CMAKE_CXX_FLAGS) -O2 -DNDEBUG (来自CMAKE_CXX_FLAGS_RELEASE)
这种设计允许我们:
- 在 CMAKE_CXX_FLAGS 中设置所有构建类型共用的选项
- 在 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE 中设置特定构建类型的选项
- 通过切换 CMAKE_BUILD_TYPE 来使用不同的编译选项组合
2.2 范例测试
lesson1/base/Version.h
让我们创建一个简单的版本头文件,用于定义库的版本信息:
#ifndef MYMUDUO_BASE_VERSION_H #define MYMUDUO_BASE_VERSION_H namespace mymuduo { inline const char* version() { return "1.0.0"; } } // namespace mymuduo #endif // MYMUDUO_BASE_VERSION_H
对应测试文件
lesson1/examples/hello-muduo.cc
#include <iostream> #include "base/Version.h" using namespace mymuduo; int main() { std::cout << "Hello, Muduo!" << std::endl; std::cout << "muduo version: " << version() << std::endl; return 0; }
要验证环境配置是否正确,可以执行以下命令:
cd mymuduo/lesson1 mkdir build cd build cmake .. make
执行命令
./bin/hello-muduo
打印:
3 小节
这一章节大致理解cmake构建框架就行,不需要一下子就读懂每一行CMakeLists.txt,这个是没有必要的,只需要理解在各个CMakeLists.txt依赖关系就行。
4 进阶参考
这里只是作为后续在使用cmake构建项目时不知道如何规划时可以作为参考,目前不用花时间去研究。
4.1 CMake常见目录变量及其区别
PROJECT_SOURCE_DIR
- 表示当前项目的源代码根目录
- 指向包含顶层CMakeLists.txt的目录
- 在我们的项目中,指向lesson1目录
其他常见CMake目录变量
1.CMAKE_SOURCE_DIR
- 表示整个源代码树的根目录
- 指向最顶层的CMakeLists.txt所在目录
- 在单项目中与PROJECT_SOURCE_DIR相同,在多项目中指向最顶层项目
2.CMAKE_CURRENT_SOURCE_DIR
- 表示当前处理的CMakeLists.txt所在的目录
- 在子目录中会改变,如在lesson1/base/CMakeLists.txt中指向lesson1/base
3.PROJECT_BINARY_DIR
- 表示当前项目的构建目录
- 在我们的项目中,指向lesson1/build
4.CMAKE_BINARY_DIR
- 表示整个构建树的根目录
- 指向CMake执行的目录
- 在单项目中与PROJECT_BINARY_DIR相同
5.CMAKE_CURRENT_BINARY_DIR
- 表示当前处理的CMakeLists.txt对应的构建目录
- 在子目录中会改变,如在lesson1/base/CMakeLists.txt中指向lesson1/build/base
6.CMAKE_INSTALL_PREFIX
- 表示安装目标的根目录
- 默认为/usr/local(Unix系统)或c:/Program Files/${PROJECT_NAME}(Windows系统)
7.CMAKE_MODULE_PATH
- 表示CMake模块的搜索路径
- 用于查找额外的CMake模块
8.CMAKE_CURRENT_LIST_DIR
- 表示当前正在处理的CMake文件的完整目录
- 与CMAKE_CURRENT_SOURCE_DIR类似,但在include()命令中更可靠
框图说明
4.2 我们项目中的实际应用
在muduo项目中的目录变量具体值
muduo项目中CMake目录变量的具体值(文本形式)
- CMAKE_SOURCE_DIR: /home/lqf/long/spark_muduo/lesson1
- PROJECT_SOURCE_DIR: /home/lqf/long/spark_muduo/lesson1
- CMAKE_CURRENT_SOURCE_DIR:
- 根目录CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1
- base/CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1/base
- net/CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1/net
- CMAKE_BINARY_DIR: /home/lqf/long/spark_muduo/lesson1/build
- PROJECT_BINARY_DIR: /home/lqf/long/spark_muduo/lesson1/build
- CMAKE_CURRENT_BINARY_DIR:
- 根目录CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1/build
- base/CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1/build/base
- net/CMakeLists.txt中: /home/lqf/long/spark_muduo/lesson1/build/net
- EXECUTABLE_OUTPUT_PATH: /home/lqf/long/spark_muduo/lesson1/build/bin
- LIBRARY_OUTPUT_PATH: /home/lqf/long/spark_muduo/lesson1/build/lib
这些变量在CMake构建系统中帮助我们引用正确的路径,使项目构建更加灵活和可移植。
#实习##校招##c++后端##牛客创作赏金赛#