【Bug已解决】[Build] Wrong ORT version in VersionInfo 解决方案

📅 2026/8/13 12:10:49
【Bug已解决】[Build] Wrong ORT version in VersionInfo 解决方案
【Bug已解决】[Build] Wrong ORT version in VersionInfo 解决方案一、现象长什么样编译 ONNX Runtime 后运行时通过OrtGetApiBase()→GetVersionString()或Ort::GetVersionString()拿到的版本号和CMakeLists.txt/build_info里声明的版本不一致——比如实际编的是 1.24.0但GetVersionString()返回1.23.0或一串错误占位符。现象# 现象 A运行时版本号落后于实际构建版本 # cmake -DORT_VERSION1.24.0 编出来的库GetVersionString() 返回 1.23.0 # 用户在 Java/Python 里打印 ort.get_version() 对不上排查半天 # 现象 B版本号是占位符/空 # GetVersionString() 返回 0.0.0 或 BUILD_VERSION # 说明 VersionInfo 生成脚本没正确注入版本 # 现象 C只在特定构建配置触发 # 比如从源码打 nightly 包时版本注入脚本的路径/变量名改了 # 稳定版 release 正常 —— 定位到 VersionInfo 的生成步骤最坑的是现象 A版本号“看起来合理只是旧一点”不会报错但会让用户误判自己用的版本、装错配套组件排查极费时。二、背景ONNX Runtime 的版本信息通常由一个代码生成步骤产出CMake 在配置阶段读取ORT_VERSION或version.txt生成一个version_info.h含ORT_VERSION_STRING宏再被VersionInfo源文件引用GetVersionString()返回它。问题出在生成步骤读取版本的变量名/来源和 CMake 实际设置的不一致。比如 CMake 把版本存在ORT_VERSION_MAJOR/MINOR/PATCH或直接ORT_VERSION而生成脚本读的是另一个名字如PROJECT_VERSION导致脚本读到了旧的/默认的版本写进了version_info.h。或者生成脚本在cmake重新配置时没重新运行缓存沿用了上一次的旧版本。这是构建系统审查里典型的坑版本这类“生成式常量”的注入源与消费源不一致且生成步骤可能因缓存而过期。三、根因版本变量名不一致生成脚本读的变量名和 CMake 设置的对不上读到默认值/旧值 → 现象 A。生成步骤缓存未失效version_info.h的生成被标记为“无需重跑”依赖写错CMake 重新配置改了版本后旧的version_info.h没更新 → 现象 A/B。缺少“运行时版本 构建版本”的对拍CI 只测功能从不在运行时断言GetVersionString()等于构建声明版本回归长期存在。本质是VersionInfo 的生成源与 CMake 设置不一致 生成步骤缓存未失效 缺版本对拍导致运行时版本号错误。四、最小可运行复现下面用 Python 模拟“生成脚本读的变量名与 CMake 设置不一致导致版本错位”def generate_version_info_buggy(cmake_vars: dict): buggy: 生成脚本读 PROJECT_VERSION但 cmake 设的是 ORT_VERSION。 # 生成脚本以为版本在 PROJECT_VERSION version cmake_vars.get(PROJECT_VERSION, 0.0.0) # 没设置 - 默认 return f#define ORT_VERSION_STRING {version} def generate_version_info_fixed(cmake_vars: dict): fixed: 读 cmake 实际设置的 ORT_VERSION。 version cmake_vars.get(ORT_VERSION) or \ f{cmake_vars.get(ORT_VERSION_MAJOR,0)}. \ f{cmake_vars.get(ORT_VERSION_MINOR,0)}. \ f{cmake_vars.get(ORT_VERSION_PATCH,0)} return f#define ORT_VERSION_STRING {version} cmake {ORT_VERSION: 1.24.0} # 实际设置 print(buggy:, generate_version_info_buggy(cmake)) # #define ... 0.0.0 print(fixed:, generate_version_info_fixed(cmake)) # #define ... 1.24.0buggy输出0.0.0读不到fixed正确读到1.24.0。五、解决方案第一层最小直接修复最小修复让版本生成脚本读 CMake实际设置的变量并把生成步骤的依赖写对版本变就必须重生成# CMake 侧显式把版本传给生成脚本 set(ORT_VERSION 1.24.0 CACHE STRING ORT version) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/version_info.h.in ${CMAKE_CURRENT_BINARY_DIR}/version_info.h ONLY # 用 ORT_VERSION 注入 )version_info.h.in#define ORT_VERSION_STRING ORT_VERSION这一层改动最小configure_file(ONLY)用 CMake 变量直接注入且 CMake 会在ORT_VERSION变化时重新生成。但依赖“每处版本源都一致”下看第二层。六、解决方案第二层结构性改进把“ORT 版本如何从构建配置正确流入运行时 VersionInfo”固化成单一事实来源。下面这个 dataclass 集中管理版本解析契约构建脚本和运行时校验都引用同一套规则from dataclasses import dataclass, field from typing import Dict, Optional dataclass class OrtVersionInfoPolicy: 单一事实来源ORT 版本从构建配置到运行时的注入契约。 major: int 0 minor: int 0 patch: int 0 property def version_string(self) - str: return f{self.major}.{self.minor}.{self.patch} classmethod def from_cmake(cls, vars: Dict[str, str]) - OrtVersionInfoPolicy: 唯一解析入口从 CMake 实际设置的变量读取缺省报错而非用 0.0.0。 if ORT_VERSION in vars: m, mi, p (vars[ORT_VERSION].split(.) [0, 0, 0])[:3] return cls(int(m), int(mi), int(p)) # 退化从 MAJOR/MINOR/PATCH 读若都没有必须显式报错 if ORT_VERSION_MAJOR in vars: return cls(int(vars[ORT_VERSION_MAJOR]), int(vars.get(ORT_VERSION_MINOR, 0)), int(vars.get(ORT_VERSION_PATCH, 0))) raise ValueError(no ORT version variable set in cmake config) def render_header(self) - str: return f#define ORT_VERSION_STRING {self.version_string}这一层的关键收益单一解析入口from_cmake只读 CMake 实际变量不读错位名字杜绝现象 A缺省即报错读不到版本直接ValueError不用0.0.0掩盖杜绝现象 B可渲染render_header产出version_info.h内容构建与校验同源单一事实来源所有版本注入约定收口在OrtVersionInfoPolicy。七、解决方案第三层断言 / CI 守护把第二层钉成 pytest挂进 CI确保运行时版本 构建版本import pytest from your_package.ort_version_info import OrtVersionInfoPolicy def test_from_cmake_reads_actual_var(): # 断言 1从 ORT_VERSION 正确读出不读错位变量 p OrtVersionInfoPolicy.from_cmake({ORT_VERSION: 1.24.0}) assert p.version_string 1.24.0 def test_missing_version_raises(): # 断言 2没有任何版本变量必须报错不用 0.0.0 掩盖 with pytest.raises(ValueError): OrtVersionInfoPolicy.from_cmake({}) def test_header_render_matches(): # 断言 3渲染的 header 含正确版本字符串 p OrtVersionInfoPolicy.from_cmake({ORT_VERSION: 1.24.0}) assert 1.24.0 in p.render_header() def test_runtime_matches_build(): # 断言 4端到端运行时 GetVersionString 应等于构建声明版本 build OrtVersionInfoPolicy.from_cmake({ORT_VERSION: 1.24.0}) runtime_str build.version_string # 模拟 GetVersionString() assert runtime_str 1.24.0四条断言从“读实际变量”“缺失报错”“header 正确”“运行时匹配构建”四面把版本错位钉死在 CI。八、排查清单编译 ONNX Runtime 后GetVersionString()不对时运行时版本落后于构建版本查版本生成脚本读的变量名是否和 CMake 设置的一致现象 A。版本是0.0.0/ 占位符生成步骤没注入或读了默认查configure_file依赖。改了ORT_VERSION重新编译版本没变生成步骤缓存未失效version_info.h没重生成。用第二层OrtVersionInfoPolicy唯一解析入口 缺失即报错 可渲染 header。加第三层 pytest断言“读实际变量、缺失报错、header 正确、运行时匹配构建”。版本这类生成式常量必须在运行时和构建声明做对拍否则错位无声。九、小结ORT 的VersionInfo版本错位 bug 本质是版本生成脚本读取的变量名与 CMake 实际设置的不一致且生成步骤可能因缓存未失效而沿用旧版本导致运行时GetVersionString()返回落后/错误的版本号且因不报错、只“旧一点”极难自查。修复分三层——第一层用configure_file(ONLY)直接注入 CMake 变量并依赖正确重生成第二层用OrtVersionInfoPolicy这个 dataclass 把版本解析收口成单一入口、缺失即报错、可渲染 header第三层用四条 pytest 把“读实际变量、缺失报错、header 正确、运行时匹配构建”钉死在 CI。核心心法版本这类生成式常量注入源与消费源必须同名同源缺失必须显式报错而非用占位默认值且必须在运行时与构建声明做对拍。