ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

TDengine 构建体系中的 cppstub Conan 包:header-only C++ 打桩库的打包、验证与集成实践

TDengine 构建体系中的 cppstub Conan 包:header-only C++ 打桩库的打包、验证与集成实践 TDengine 构建体系中的 cppstub Conan 包header-only C 打桩库的打包、验证与集成实践【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine本指南以 TDengine 仓库中 conan/cppstub/SUMMARY.md 为核心完整梳理 cppstubcpp-stub这一 C 单元测试打桩库从源码收编为 Conan 包、本地创建验证、再到接入 TDengine 主工程构建体系的全过程。读者将掌握 header-only Conan 包的标准写法含平台差异化头文件选择、package_id清理、no_copy_source优化理解conan create/conan list的验证闭环并能在自己的 C 测试工程里通过cppstub::cppstub一行链接即可使用stub.h与addr_any.h。一、背景TDengine 测试体系为什么需要 cppstubTDengine 的单元测试大量依赖函数打桩stub能力被测代码中调用真实函数的位置需要被临时替换为桩函数以隔离外部依赖、验证分支逻辑。在仓库源码中可以找到大量直接证据——分布在 util、catalog、executor、parser、qworker、scalar、scheduler、new-stream、dnode、index、monitor 等多个测试目录下的文件都直接引入了打桩头文件例如source/util/test/memPoolTest.cppsource/libs/catalog/test/catalogTests.cppsource/libs/executor/test/joinTests.cppsource/libs/executor/test/queryPlanTests.cppsource/libs/parser/test/mockCatalog.cppsource/libs/qworker/test/qworkerTests.cppsource/libs/scalar/test/filter/filterTests.cppsource/libs/scalar/test/scalar/scalarTests.cppsource/libs/scheduler/test/schedulerTests.cpp这些文件此前依赖 CMakeExternalProject从上游 GitHub 拉取源码编译而本次迁移的目标是将 cppstub 收编为一个标准 Conan 包纳入 TDengine 的 Conan 依赖体系统一管理。二、包概况与核心特性根据 conan/cppstub/SUMMARY.md该 Conan 包的基本信息如下项目值包名Package Namecppstub版本Version1.0.0类型header-only纯头文件库无需编译与链接上游来源cpp-stubcoolxv/cpp-stub固定 Commit3137465194014d66a8402941e80d2bccc6346f51许可证MIT见 conan/cppstub/cppstub/LICENSE包的四大特性Header-Only不包含任何编译产物消费方无需处理链接库顺序或 ABI 兼容问题平台自适应打包阶段根据目标操作系统自动挑选正确的addr_any.hLinux / macOS / Windows 三套实现自带验证test_package/测试包可验证头文件在消费方工程中可被正确找到与编译跨平台覆盖 Linux、macOS、Windows 三大平台。三、包目录结构与文件清单仓库中 conan/cppstub 目录的完整结构如下conan/cppstub/ ├── conanfile.py # Conan recipe打包脚本 ├── README.md # 包文档 ├── SUMMARY.md # 迁移总结本文主体 ├── cppstub/ # 上游源码快照 │ ├── LICENSE # MIT 许可证 │ ├── src/ │ │ └── stub.h # 核心打桩头文件Stub 类与平台宏 │ ├── src_linux/ │ │ └── addr_any.h # Linux 专用地址操纵头文件基于 ELFIO │ ├── src_darwin/ │ │ └── addr_any.h # macOS 专用实现 │ └── src_win/ │ └── addr_any.h # Windows 专用实现 └── test_package/ # 消费方验证工程 ├── conanfile.py ├── CMakeLists.txt └── test_package.cpp其中stub.h是打桩功能的实现主体addr_any.h提供取函数真实地址的平台相关能力——注意它是平台无关打桩的关键在 Linux/macOS 上基于 ELF 解析src_linux/addr_any.h内含内联的 ELFIO 3.8 头文件集共 5000 余行在 Windows 上则依赖 PE 相关机制。四、Conan recipe 源码级解析包的核心打包逻辑位于 conan/cppstub/conanfile.py短短 69 行实现了完整的 header-only 包。逐段拆解如下。4.1 元信息声明class CppStubConan(ConanFile): name cppstub version 1.0.0 license MIT url https://github.com/coolxv/cpp-stub description A simple and easy-to-use C stub library for unit testing topics (cpp, stub, testing, mock) settings os, compiler, build_type, arch no_copy_source Trueno_copy_source True是 header-only 包的关键优化告诉 Conan 源码无需在构建目录中复制直接以导出目录为 source 目录显著减少磁盘占用与拷贝开销。4.2 export_sources源码随 recipe 一并导出def export_sources(self): copy(self, *, srcos.path.join(self.recipe_folder, cppstub), dstos.path.join(self.export_sources_folder, cppstub))export_sources把 recipe 旁cppstub/目录中的全部源码stub.h、三份addr_any.h、LICENSE随 recipe 一起导出到 Conan 本地缓存保证离线可复现source()方法留空表示不再从网络下载完全依赖本地导出。这也正是 SUMMARY.md 中Source files downloaded from GitHub raw URLs using wget的历史做法被替换后的新方案——源码直接以快照形式入库。4.3 package_idheader-only 与平台无关def package_id(self): # This is a header-only library self.info.clear()由于package_id()清空了全部设置项任何操作系统、编译器、构建类型、架构下生成的包 ID 完全一致同一个二进制缓存可被所有消费配置复用。4.4 package按目标平台挑选 addr_any.hif self.settings.os Linux: platform_dir src_linux elif self.settings.os Macos: platform_dir src_darwin elif self.settings.os Windows: platform_dir src_win else: platform_dir src_linux # Default to Linuxpackage()阶段做三件事将LICENSE/README.md拷入licenses/满足许可证合规要求、将src/stub.h拷入include/、再按当前settings.os从三套平台目录中选中对应的一份addr_any.h拷入include/。未匹配平台默认回退 Linux 实现。4.5 package_info纯头文件消费接口def package_info(self): self.cpp_info.bindirs [] self.cpp_info.libdirs [] self.cpp_info.includedirs [include]cpp_info中清空 bin/lib 目录不产生任何链接目标仅暴露include头文件搜索路径。Conan 会自动据此生成cppstub::cppstub的 CMake 导入目标。五、创建包与本地验证在仓库的 conan/cppstub 目录SUMMARY.md 中写作迁移者本地的TDinternal/community/conan/cppstub当前仓库对应路径即为conan/cppstub执行conan create . --buildmissingconan create .以当前目录 recipe 构建包并安装到本地 Conan 缓存--buildmissing允许在缓存缺失时现场构建header-only 场景下实际无编译动作。验证包是否成功进入本地缓存conan list cppstub/*预期输出Local Cache cppstub cppstub/1.0.0六、测试包消费方视角的验证链路test_package/模拟真实消费方是conan create自动执行的最后一步用于证明包可被下游工程正常消费。test_package/conanfile.py 声明依赖被测引用self.tested_reference_str即刚创建的cppstub/1.0.0并通过CMakeDepsCMakeToolchain生成 CMake 查找脚本然后依次cmake.configure()、cmake.build()、运行产物def requirements(self): self.requires(self.tested_reference_str) def build(self): cmake CMake(self) cmake.configure() cmake.build() def test(self): if can_run(self): cmd os.path.join(self.cpp.build.bindir, test_cppstub) self.run(cmd, envconanrun)test_package/CMakeLists.txt 展示了最标准的消费写法——这正是最终用户在自己的工程里要抄的模板cmake_minimum_required(VERSION 3.15) project(test_cppstub CXX) find_package(cppstub REQUIRED CONFIG) add_executable(test_cppstub test_package.cpp) target_link_libraries(test_cppstub cppstub::cppstub)test_package.cpp 做三重断言普通调用add(2, 3)结果必须为 5stub.h头文件可用addr_any.h头文件可用。需要说明的是该测试刻意未真正触发打桩逻辑真实打桩需要更复杂的地址操纵与进程上下文仅验证头文件可被 include 且编译链接通过——这是 Conan 测试包的常见策略验证打包正确性而非库的功能完备性。SUMMARY.md 记录的实际运行输出Testing cppstub package... Normal add(2, 3) 5 stub.h header is available addr_any.h header is available All tests passed!七、在业务项目中使用 cppstub7.1 声明依赖conanfile.txt方式[requires] cppstub/1.0.0 [generators] CMakeDeps CMakeToolchainconanfile.py方式def requirements(self): self.requires(cppstub/1.0.0)7.2 链接目标find_package(cppstub REQUIRED CONFIG) target_link_libraries(your_target cppstub::cppstub)7.3 引入头文件#include stub.h #include addr_any.h由于是 header-only 包target_link_libraries实际只传播头文件搜索路径不产生任何.a/.lib链接依赖。八、集成到 TDengine 主工程构建体系SUMMARY.md 给出了把该包接入 TDengine 主构建的完整三步计划且当前仓库已经落地了大部分代码。8.1 更新 cmake/conan.cmakecmake/conan.cmake 中已经实现了原先为空的DEP_ext_cppstub系列宏macro(DEP_ext_cppstub tgt) # cppstub is now available as a Conan package (header-only library) if(TARGET cppstub::cppstub) target_link_libraries(${tgt} PUBLIC cppstub::cppstub) endif() endmacro() macro(DEP_ext_cppstub_INC tgt) if(TARGET cppstub::cppstub) target_link_libraries(${tgt} INTERFACE cppstub::cppstub) endif() endmacro() macro(DEP_ext_cppstub_LIB tgt) ... endmacro()同时在文件开头cmake/conan.cmake通过find_package(cppstub QUIET)探测 Conan 缓存中的包未找到时回退到ExternalProject方案。8.2 在 conanfile.txt/conanfile.py 中声明在主工程的 Conan 配置中加入条件依赖——仅在开启测试构建时引入def requirements(self): if self.options.get_safe(build_tests): self.requires(cppstub/1.0.0)8.3 与既有 ExternalProject 方案的关系值得注意的是cmake/external.cmake 中仍保留着 cppstub 的ExternalProject_Add回退实现它按平台设置src_linux/src_darwin/src_win目录从固定 Commit 的 tarball 下载源码并通过copy_if_different将src/stub.h与对应平台的addr_any.h复制到安装目录的include/下。这与 Conan 包package()阶段做的事完全等价——从源码结构可以推断Conan 包正是这段 ExternalProject 逻辑的Conan 化封装二者共用同一套平台目录划分约定为 Conan 缓存未命中时提供无缝降级。8.4 恢复测试文件中的 addr_any.h 包含依赖打通后SUMMARY.md 建议取消以下测试文件中#include addr_any.h一行的注释仓库现状是均已放开可直接核对source/util/test/memPoolTest.cppsource/libs/catalog/test/catalogTests.cppsource/libs/executor/test/joinTests.cppsource/libs/executor/test/queryPlanTests.cppsource/libs/parser/test/mockCatalog.cppsource/libs/qworker/test/qworkerTests.cppsource/libs/scalar/test/filter/filterTests.cppsource/libs/scalar/test/scalar/scalarTests.cppsource/libs/scheduler/test/schedulerTests.cpp九、stub.h 底层实现剖析作为包的灵魂conan/cppstub/cppstub/src/stub.h 的实现揭示了这套 header-only 打桩库的工作原理也解释了为何它需要平台差异化的addr_any.h。9.1 核心思路运行时改写函数入口Stub类的set()方法stub.h流程为用addrof()取出原函数与桩函数的真实地址 → 通过distanceof()判断二者距离是否超过 32 位相对跳转范围 → 用mprotect/VirtualProtect将函数所在内存页改为可写 → 在函数入口处写入跳转指令远跳转REPLACE_FAR或近跳转REPLACE_NEAR→ 恢复页为只读可执行。reset()stub.h则把入口处的原始字节从code_buf中拷回clear()stub.h在析构时批量还原所有打桩点。9.2 平台指令级支持stub.h通过预定义宏覆盖了极为广泛的指令集每个平台都定义了CODESIZE跳转指令字节数与REPLACE_FAR/REPLACE_NEAR宏x86/x64默认分支stub.h远跳转用 13 字节的movabs %r11, imm64; jmpq *%r11近跳转用 5 字节的E9 rel32AArch64stub.h16 字节的ldr x9, 8; br x9加 8 字节目标地址此外还包括 ARM8 字节、Thumb12 字节、MIPS6480 字节需手动入栈出栈、RISC-V 32/64、LoongArch64、PowerPC64、Alpha、SPARC64、s390x、sw_64 等。所有平台在改写完成后统一执行CACHEFLUSH非 Windows 用__builtin___clear_cacheWindows 用FlushInstructionCache若定义了__VALGRIND__还会追加VALGRIND_DISCARD_TRANSLATIONS保证动态翻译缓存一致。9.3 内存页保护与地址操纵Stub构造函数通过sysconf(_SC_PAGE_SIZE)Windows 为GetSystemInfo获取页大小pageof()将函数地址对齐到页边界后交给mprotect处理stub.h。而取函数真实地址这件事正是addr_any.h的职责——Linux 版内联了完整 ELFIO 库用于解析 ELF 符号表从而把类成员函数指针/重载函数名解析为可改写的代码地址。十、与 fast-lzma2 包的横向对比SUMMARY.md 将本包与仓库中同为迁移产物的 conan/fast-lzma2 做了对比可从其 README.md 佐证相同点均通过export_sources将源码随 recipe 导出保证可复现构建均包含test_package消费方验证均提供完整 README 文档均保持平台无关的二进制缓存复用。差异点cppstub 是 header-only完全跳过 build 阶段比 fast-lzma2需要真实编译的压缩库更轻量cppstub 的平台差异体现在打包期文件选择三选一拷贝addr_any.h而非编译期宏切换包体积与构建耗时显著更小。十一、注意事项与限制基于 SUMMARY.md 的 Notes 与源码核实使用本包需留意以下几点no_copy_source True源码只在导出目录维护一份任何在构建期修改源码的 recipe 模式都不适用package_id()清理包 ID 与平台解耦不同 OS 下conan create出的包可互相复用缓存但若后续package()中的平台文件选择逻辑发生变化务必同步调整package_id策略否则可能命中错误的平台头文件源码获取方式上游源码以快照形式存放在仓库 conan/cppstub/cppstub 内固定 Commit3137465...不再依赖运行时网络下载测试覆盖边界test_package仅验证头文件可访问性并不覆盖 Stub 打桩功能的实际行为如需验证真实打桩效果应参考 source/libs/executor/test/joinTests.cpp 等仓库内测试用例回退路径Conan 缓存未命中时cmake/external.cmake 的ExternalProject_Add仍是可用后备二者产物等价。结语从一份迁移总结出发本文结合仓库内 conanfile.py、test_package、stub.h 与 cmake/conan.cmake、cmake/external.cmake 的既有实现完整还原了 cppstub 的 Conan 化路径header-only recipe 的写法要点、平台差异化头文件的打包选择、消费方三行接入、以及主工程构建体系的集成与回退策略。这套本地快照 export_sources test_package 验证 主工程宏对接的模式同样适用于将其他第三方测试依赖收编进 TDengine Conan 体系是一份可直接复用的打包范本。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表