当前位置: 首页 > news >正文

宏定义看开源的依赖地狱:Drogon vs Trantor | a fix pr

在开源软件开发中,依赖管理是一个一直被谈论的话题,像docker, cargo, go mod,nuget 都是为了解决这一问题应运而生,话不多说,我们来看最近在 Drogon 中发现的这个问题叭

回顾

在使用 Conan 包管理器构建 Drogon 1.9.11 时遇到了编译错误:

/home/gav2xlin/.conan2/p/b/drogo9f0dcc0bb5db8/b/src/lib/src/HttpResponseImpl.cc:In member function ‘std::shared_ptrtrantor::MsgBuffer drogon::HttpResponseImpl::renderToBuffer():/home/gav2xlin/.conan2/p/b/drogo9f0dcc0bb5db8/b/src/lib/src/HttpResponseImpl.cc:657:54:error:‘MICRO_SECONDS_PRE_SEC’ wasnotdeclared inthisscope657|((now.microSecondsSinceEpoch()/MICRO_SECONDS_PRE_SEC)!=|^~~~~~~~~~~~~~~~~~~~~[101/148]Building CXX object CMakeFiles/drogon.dir/lib/src/IntranetIpFilter.cc.o

问题的根源在于:

  • Trantor 1.5.25(Drogon 的底层依赖库) 将宏MICRO_SECONDS_PRE_SEC重命名为MICRO_SECONDS_PER_SEC
  • Drogon 1.9.11仍在多处使用旧的宏名称
  • 结果:编译失败

细节

在 Trantor 的Date.h中,原本的定义是:

#defineMICRO_SECONDS_PRE_SEC1000000LL// 已删除

而 Drogon 的多个文件中仍在使用:

// HttpResponseImpl.ccautonowSecond=now.microSecondsSinceEpoch()/MICRO_SECONDS_PRE_SEC;// Utilities.ccreturntrantor::Date(epoch*MICRO_SECONDS_PRE_SEC);// HttpDateTest.ccCHECK(date.microSecondsSinceEpoch()/MICRO_SECONDS_PRE_SEC==1591348778);

(用过rust和go的包管理后 再写c++…😎😭

(&对于依赖地域这个话题 之前写过一篇文章 好像还没发 有时间整理同步过来…

分析

  1. 破坏性变更的隐蔽性

这个案例揭示了一个重要问题:即使是修正拼写错误这样看似无害的改动,也可能成为破坏性变更

PREPER的修改从语义上是正确的(per second 而非 pre second),但它违反了一个基本原则:

向后兼容性优先于代码美学

正确的做法应该是:
// 方案 1: 保留旧宏并标记为废弃#defineMICRO_SECONDS_PRE_SEC1000000LL// @deprecated Use MICRO_SECONDS_PER_SEC#defineMICRO_SECONDS_PER_SEC1000000LL// 方案 2: 使用 C++ 的类型别名namespacetrantor{constexprint64_tMICRO_SECONDS_PER_SEC=1000000LL;[[deprecated("Use MICRO_SECONDS_PER_SEC")]]constexprint64_tMICRO_SECONDS_PRE_SEC=MICRO_SECONDS_PER_SEC;}

2. 语义化版本控制的重要性

根据 Semantic Versioning 2.0.0:

  • MAJOR(主版本): 不兼容的 API 变更
  • MINOR(次版本): 向后兼容的功能新增
  • PATCH(补丁版本): 向后兼容的问题修正

Trantor 从 1.5.23 到 1.5.25 只增加了 PATCH 版本号

3. 依赖版本锁定策略

在 C++ 生态中,依赖管理比 npm、pip 等现代包管理器更为复杂:

包管理器锁定文件版本解析
npmpackage-lock.json自动
piprequirements.txt手动/自动
Conanconanfile.lock需配置
vcpkgvcpkg.json基于 baseline

Conan 的版本范围语法:

# conanfile.pydefrequirements(self):# ❌ 危险:允许任何 1.5.x 版本self.requires("trantor/[>=1.5.0 <1.6.0]")# ✅ 安全:锁定到已知可用版本self.requires("trantor/1.5.23")# ✅ 更好:使用锁定文件# conan lock create . --lockfile-out=conan.lock

开源协作

对上游库维护者(Trantor)

1.变更影响分析

在发布前使用工具检测 API 变更:

# 使用 abi-compliance-checkerabi-compliance-checker -lib trantor\-old v1.5.23.xml -new v1.5.25.xml# 使用 include-what-you-useiwyu_tool.py -p.>iwyu.log
2.渐进式废弃策略
// Version 1.5.24: 添加新宏,保留旧宏#defineMICRO_SECONDS_PER_SEC1000000LL#defineMICRO_SECONDS_PRE_SECMICRO_SECONDS_PER_SEC// Deprecated// Version 1.6.0: 添加编译警告#ifdef__GNUC__#pragmaGCC warning"MICRO_SECONDS_PRE_SEC is deprecated"#endif// Version 2.0.0: 完全移除// (only MICRO_SECONDS_PER_SEC exists)
3.详细的变更日志
## [1.5.25] - 2024-XX-XX ### ⚠️ Breaking Changes - **DEPRECATED**: `MICRO_SECONDS_PRE_SEC` macro renamed to `MICRO_SECONDS_PER_SEC` - **Impact**: Projects using the old macro will fail to compile - **Migration**: Replace all occurrences with the new name - **Affected files**: `trantor/utils/Date.h` ### Migration Guide ```cpp // Before auto seconds = time / MICRO_SECONDS_PRE_SEC; // After auto seconds = time / MICRO_SECONDS_PER_SEC;

对下游项目(Drogon)

1.持续集成测试
# .github/workflows/ci.ymlname:CI with Multiple Trantor Versionson:[push,pull_request]jobs:test:strategy:matrix:trantor-version:['1.5.23','1.5.24','1.5.25']steps:-name:Install Trantor ${{matrix.trantor-version}}run:conan install trantor/${{matrix.trantor-version}}-name:Build and Testrun:|cmake --build build ctest --test-dir build
2.依赖版本范围声明

conanfile.py中明确兼容性:

classDrogonConan(ConanFile):name="drogon"version="1.9.11"defrequirements(self):# 明确声明兼容的 Trantor 版本self.requires("trantor/[>=1.5.0 <1.5.25]")defvalidate(self):# 运行时验证trantor_version=self.dependencies["trantor"].ref.versioniftrantor_version>="1.5.25":self.output.warn("Trantor >= 1.5.25 has breaking changes. ""Please use Trantor 1.5.23 or wait for Drogon update.")
3.快速响应机制
// 临时兼容层 (compatibility_shim.h)#ifndefMICRO_SECONDS_PER_SEC#ifdefMICRO_SECONDS_PRE_SEC#defineMICRO_SECONDS_PER_SECMICRO_SECONDS_PRE_SEC#else#defineMICRO_SECONDS_PER_SEC1000000LL#endif#endif

启示

1. C++ 生态的挑战

与 Rust、Go 等现代语言相比,C++ 面临独特的依赖管理挑战:

特性C++RustGo
包管理器多种…(Conan/vcpkg/Hunter)Cargo(官方)go mod(官方)
ABI 稳定性编译器相关稳定稳定
版本解析复杂自动自动
头文件依赖需要

2. 依赖地狱的类型

┌─────────────────────────────────────┐ │ 依赖地狱的四种形态 │ ├─────────────────────────────────────┤ │ 1. 版本冲突 (Version Conflict) │ │ A → B@1.0, C → B@2.0 │ │ │ │ 2. 钻石依赖 (Diamond Dependency) │ │ A → B → D@1.0 │ │ A → C → D@2.0 │ │ │ │ 3. 破坏性变更 (Breaking Change) ⭐ │ │ 本案例:Trantor 改名宏定义 │ │ │ │ 4. 传递依赖 (Transitive Dependency) │ │ A → B → C → D (深层依赖链) │ └─────────────────────────────────────┘

关于钻石依赖 在多态部分也有提到过–棱形依赖

前文传送:

[C++#28][多态] 两个条件 | 虚函数表 | 抽象类 | override 和 final | 重载 重写 重定义

3. 解决方案

问题短期方案长期方案
版本不兼容锁定依赖版本语义化版本 + CI
API 变更兼容层/Shim废弃策略
文档缺失Issue 跟踪自动化文档
测试不足手动测试矩阵测试

建议

对于库维护者

# 1. 使用 API 变更检测工具$gitdiffv1.5.23 v1.5.25 -- include/|grep-E"^-.*#define"# 2. 自动化兼容性测试$ ./scripts/test-downstream-projects.sh# 3. 生成迁移指南$ ./scripts/generate-migration-guide.sh v1.5.23 v1.5.25

对于库使用者

# conanfile.py - 防御性编程classMyProject(ConanFile):defrequirements(self):# 使用版本范围并排除已知问题版本self.requires("trantor/[>=1.5.0 <1.5.25 || >=1.5.26]")defconfigure(self):# 启用严格模式self.options["trantor"].strict_abi=True

通用工具链

# 依赖分析$ conan info.--graph=deps.html $ cmake --graphviz=deps.dot.# 版本锁定$ conan lock create.--lockfile-out=conan.lock $ conaninstall.--lockfile=conan.lock# ABI 检查$ abi-dumper libtrantor.so -o ABI-1.5.23.dump $ abi-compliance-checker -l trantor\-old ABI-1.5.23.dump -new ABI-1.5.25.dump

这个看似简单的宏定义重命名事件,实际上反映了开源软件开发中的挑战:

  1. 技术债务的累积:拼写错误虽小,但修正成本巨大
  2. 向后兼容的代价:完美的代码 vs 可用的代码
  3. 生态系统的脆弱性:一个小改动可能影响数百个下游项目

& callback 去年,再次歌颂一下debain

要点

DO(应该做)

  • 使用语义化版本控制
  • 提供详细的变更日志和迁移指南
  • 在 CI 中测试多个依赖版本
  • 采用渐进式废弃策略

DON’T(不应该做)

  • 在 PATCH 版本中引入破坏性变更
  • 删除公共 API 而不提供过渡期
  • 假设所有用户会立即更新
  • 忽视下游项目的兼容性

思考

在开源世界中,代码不仅仅是代码,更是一种契约。当我们修改一个公共接口时,我们实际上在修改与成千上万开发者的约定。这个案例提醒我们:

“With great power comes great responsibility.”

作为开源贡献者,我们需要在代码质量和生态稳定性之间找到平衡

参考

  • Semantic Versioning 2.0.0
  • Hyrum’s Law
  • Conan Documentation - Version Ranges
  • C++ ABI Compliance Checker

注意到另一个最近的pr fix

CI 测试矩阵中新增了 g+±9 的测试,但之前没有对应的安装步骤,导致测试失败,所以有了这个修复

统一了 CI 工作流中 g++ 编译器的安装逻辑,使 g+±9 和 g+±13 都通过同一个安装步骤处理,避免代码重复并提高可维护性。

Before (修改前):

-name:Install g++-13if:startsWith(matrix.compiler.cxx,'g++')&&matrix.compiler.ver == 13run:|sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
  • 只处理 g+±13
  • 硬编码版本号

After (修改后):

-name:Install g++if:startsWith(matrix.compiler.cxx,'g++')&&(matrix.compiler.ver == 13||matrix.compiler.ver == 9)run:|sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.compiler.ver }} ${{ matrix.compiler.ver }}
  • 同时支持 g+±9 和 g+±13
  • 使用变量替换,更通用
http://www.cnnetsun.cn/news/136607.html

相关文章:

  • presum|二分try+滑窗cnt
  • Web自动化测试:Unittest单元测试框架
  • Apache2最佳实践
  • 实力派,也可以是偶像派
  • 基于单片机的多功能万年历
  • AI搜索时代:技术演进、产业分化与深度变革
  • SGMICRO圣邦微 SGM2019-2.5YC5G/TR SC70-5 线性稳压器(LDO)
  • 一文搞懂 低功耗蓝牙BLE 中的 ATT、GATT、MTU 与 20 字节限制
  • 别让“大锅饭”逼走你的Top Sales:揭秘薪酬误差的副作用
  • 27827828
  • 12.17 vue递归组件
  • QtScrcpy高刷投屏优化指南:告别卡顿,享受流畅体验
  • 终极移动端Windows应用运行指南:从零到流畅体验
  • 大学里的网络安全专业为什么没多少人就读?
  • 信息安全和网络空间安全这2个专业怎么选?老网安告诉你答案!
  • 英语发音MP3音频库:119,376个单词标准发音完整解决方案
  • 瞄准2026:AI安全、数据隐私与云原生——网络安全趋势预测与挑战分析
  • 重磅收藏!Java程序员转AI大模型:从代码高手到AI架构师的进阶指南
  • 2026网络安全进阶路线:盘点撬动高薪的四大关键证书
  • LangGraph实战教程:构建智能旅游规划助手,深入理解AI工作流架构【值得收藏】
  • 淘宝直播弹幕采集完整指南:5分钟快速上手数据分析
  • 文本驱动可视化:5分钟掌握专业级图表制作
  • Clipper2多边形处理技术深度解析与实战应用
  • 错过再等十年:AI驱动的生物制药Agent智能实验设计新范式
  • 造纸车间的“信号指挥官”:耐达讯自动化Profinet六路集线器,让变频器“步调一致”
  • MaterialDesignInXamlToolkit实战指南:5步打造现代化WPF界面
  • 中东电商市场,正在成为中国卖家的必争之地!
  • 为什么你的边缘Agent总是部署失败?深度剖析常见陷阱与对策
  • 一文搞定前端CSS常用布局
  • Kotaemon用户行为分析插件开发教程