ONNX Runtime 中的依赖项管理

本文档以 ONNX Runtime 为重点,为 CMake 的“使用依赖项指南”提供了额外信息。ONNX Runtime 使用了许多开源 C++ 库。例如,abseil、protobuf、re2、onnx 等。有三种主要方式可以获取它们以用于 ONNX Runtime 构建:

  1. 使用 VCPKG(推荐)
  2. 从源代码构建所有内容
  3. 使用预安装包(适用于高级用户)

下面是一个快速比较

  支持网络隔离1 支持二进制缓存2 支持交叉编译 开发状态 漏洞管理
VCPKG 良好 进行中 由 ONNX Runtime 团队提供
从源代码构建所有内容 部分支持3 可以直接使用4 完全支持 由 ONNX Runtime 团队提供
使用预安装包 难以设置 有些包无法通过此方式处理 由你的包管理器提供

如果你的软件需要符合美国总统关于改善国家网络安全的行政命令 (EO) 14028,我们强烈建议使用 VCPKG。

VCPKG

什么是 VCPKG?

VCPKG 是一个由微软和 C++ 社区维护的免费开源 C/C++ 包管理器。它主要由微软的 Visual Studio 团队开发。它帮助开发人员以简单和声明式的方式管理其 C++ 依赖项。它基于 CMake,可以集成到你的 CMake 项目中,也可以在构建前单独使用。ONNX Runtime 使用前者,即清单模式(manifest mode)。

使用 VCPKG 的先决条件

有关支持的主机,请参阅 VCPKG 文档:https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/concepts/supported-hosts.md。例如,在 Ubuntu 上,你需要安装以下软件包:apt-get install git curl zip unzip pkgconfig ninja-build

如何使用 VCPKG 构建 ONNX Runtime

只需在你的构建命令中添加“–use_vcpkg”。构建脚本 (build.py) 会将一个新的 vcpkg 仓库检出到你的构建目录中,并引导 vcpkg 工具。如果你遇到任何错误,你可能需要使用以下步骤手动获取 VCPKG:

  1. 安装 Git 并运行“git clone https://github.com/microsoft/vcpkg.git”
  2. 导航到 VCPKG 目录并运行引导脚本
    • 在 Windows 上:bootstrap-vcpkg.bat
    • 在其他系统上:bootstrap-vcpkg.sh

    如果脚本找不到某些先决条件,请安装缺失的软件并重试。

  3. 将环境变量 VCPKG_INSTALLATION_ROOT 设置为 VCPKG 目录,然后回到 ONNX Runtime 源代码文件夹并再次运行构建脚本。更多详细信息请参阅:https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/get_started/includes/setup-vcpkg.md。如果你在引导 VCPKG 时遇到问题,请联系 VCPKG 团队寻求支持。

VCPKG ports、triplets 和 toolchains

VCPKG 中的一个包被称为 VCPKG port。port 的构建脚本可以在 http://github.com/microsoft/vcpkg/tree/master/ports 找到。ONNX Runtime 也有一些自定义 port,它们托管在 https://github.com/microsoft/onnxruntime/tree/main/cmake/vcpkg-ports。自定义 port 具有比官方 port 更高的优先级。port 目录中的文件包含特定于该 port 的配置。例如,是否启用 CUDA。

triplet 是一个 cmake 文件,包含应用于当前构建中所有 port 的配置。例如,是否启用 C++ 异常。它仅用于构建依赖项。它不会影响 ONNX Runtime 源代码的构建标志。在 tools/ci_build/build.py 中设置的编译器标志和 cmake 变量仅适用于 ONNX Runtime,不适用于 vcpkg port。因此,我们需要使用自定义 triplet 文件来保持设置一致。

toolchain 文件用于设置编译器/链接器等,功能更强大。ONNX Runtime 通常使用标准的 vcpkg toolchain 文件,WebAssembly 构建除外。

独特功能

与本页列出的其他解决方案相比,VCPKG 提供了一些我们非常希望拥有的独特功能

VCPKG 为交叉编译提供了更好的支持。例如,ONNX Runtime 依赖于 ONNX。ONNX 的源代码有一些 *.proto 文件。从源代码构建 ONNX 时,我们需要使用 protoc 从 *.proto 文件生成 C++ 源文件。因此,我们需要为宿主操作系统构建 protoc 和 protoc 的依赖项。例如,如果我们在 x64 机器上构建 arm64 包,我们需要为 x64 而不是 arm64 构建 protoc。而且因为 protoc 依赖于 libprotobuf,我们必须为每个 CPU 架构构建两次 libprotobuf。无论是否使用 vcpkg,都必须构建两次。CMake 不处理这种情况,这增加了我们构建系统的复杂性。现在我们可以使用 vcpkg 解决这个问题。它开箱即用,运行良好。

使用 VCPKG,我们只需要声明根依赖项。在转向 VCPKG 之前,我们需要将所有传递依赖项添加到 cmake/deps.txt 和 cmake/external 文件夹下的 cmake 文件中,以满足网络隔离要求(这样我们可以轻松找到所有下载 URL)和 组件检测 要求。现在不再需要了,因为 VCPKG 内置支持资产缓存和 SBOM 生成

VCPKG 强制一个库只能有一个版本。例如,onnxruntime_provider_openvino.dll 和 onnxruntime.dll 使用的 protobuf 库必须完全相同。虽然这比必要的更严格,但它有助于防止 ODR 违规问题。与处理因使用同一库的多个版本而产生的潜在冲突和不一致相比,它提供了更多好处。

限制

目前,对 vcpkg 的支持仍在开发中。它不支持以下场景:

  1. 最小化构建
  2. iOS 构建
  3. Windows WebGPU 原生构建

此外,一些依赖项尚未由 VCPKG 管理。例如,Dawn。

当为 WebAssembly 构建时,它假定“–enable_wasm_simd”标志和“–enable_wasm_threads”标志始终已设置。它支持的构建变体比第二种模式(从源代码构建所有内容)少得多

此外,在此模式下,用于运行 tools/ci_build/build.py 的 Python 解释器可能与用于构建 VCPKG port 的解释器不同。这种不一致可能会导致问题。因此,如果你有多个 Python 安装,我们建议将所需的版本添加到 PATH 的开头,以将其设置为默认值。

它尚不支持设置 VC 工具集版本或 Windows SDK 版本。

对 Windows ARM64EC(包括 ARM64X)的支持是实验性的,尚未经过充分测试。

标准 cmake 有 4 种不同的构建类型(Debug、Release、RelWithDebInfo 和 MinSizeRel),而 vcpkg 只支持两种。因此,当你为 RelWithDebInfo 或 MinSizeRel 构建 ONNX Runtime 时,可能会发现二进制文件大小增加。这个问题可以通过在自定义 triplet 文件中进行更多自定义来解决。

我是 EP 开发者。我是否必须将所有依赖项转换为 vcpkg port?

如果依赖项被微软发布的 ONNX Runtime 发布包使用,那么肯定需要。否则我们可以逐案讨论。

更新 VCPKG port 的过程

首先,请检查该 port 是否是 cmake/vcpkg/vcpkg-ports 目录中的自定义 port。如果是,你需要在此处更新它。并且至少需要更新两个地方:vcpkg.json 文件中的版本号,以及 ports.cmake 文件中的 SHA512 散列值。如果你不知道要放入哪个 SHA512 值,你可以稍微修改当前散列值(翻转几个字节),然后使用“–use_vcpkg”标志构建 ONNX Runtime(不要使用额外的标志来启用任何资产缓存),然后 vcpkg 将生成一条错误消息,告诉你它期望的实际散列值。你可能还需要更新补丁文件。你可以克隆库的仓库,然后检出你正在更新的新版本,然后应用旧的补丁文件,解决冲突,最后使用“git diff”生成一个新的补丁文件来覆盖现有的补丁文件。

如果依赖项来自 VCPKG 的官方注册表(在 https://github.com/microsoft/vcpkg 中),那会容易得多。只需打开 vcpkg-configuration.json 并将基线提交 ID 更新为最新的 vcpkg 提交 ID。

然后创建一个 PR。ONNX Runtime 开发团队的成员将审查你的更改,将依赖项复制到内部位置并触发拉取请求管道。如果一切顺利,我们将合并你的更改。

从源代码构建所有内容

在你的构建命令中添加“–cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER”。当 VCPKG 未启用时,我们使用 CMake 的 FetchContent 管理依赖项。所有此类依赖项都列在 cmake/deps.txt 中,允许你自定义版本和下载 URL。这对于满足网络隔离要求或升级/降级库版本非常有用。在 ONNX Runtime 的 CMake 文件中声明依赖项时,如果提供了 FIND_PACKAGE 参数,FetchContent 将使用 CMake 的 FindPackage 模块从系统位置查找依赖项。添加 –cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER 以禁用此行为。

使用预安装包

这是从源代码构建 ONNX Runtime 时的默认模式。如果构建既没有‘–use_vcpkg’也没有‘–cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER’,它将处于此模式。这是因为 FetchContent 是各种 CMake 依赖提供程序的包装器。默认情况下,如果为依赖项提供了 FIND_PACKAGE 参数,它倾向于使用 find_package。如果你将 ONNX Runtime 集成到包管理器(如 dnf)中,你需要使用这种方法。

然而,它有一些注意事项

  1. ONNX Runtime 为依赖项提供了本地补丁,这些补丁将不会应用于你预安装的库。大多数补丁对于基本功能来说不是必需的。
  2. 如果你安装的库版本与 ONNX Runtime 期望的不同,构建脚本无法警告你。这可能会导致奇怪的构建失败。
  3. 每个库都可以以不同的方式构建。例如,ONNX Runtime 期望 ONNX 是使用“–DONNX_DISABLE_STATIC_REGISTRATION=ON”构建的。如果你从其他地方获得了预构建的 ONNX 库,很可能它不是以这种方式构建的。

因此我们说它适用于高级用户。

  1. ONNX Runtime 可以在隔离的网络环境(无需访问公共互联网)中构建吗? 

  2. 如果依赖库保持不变,它们是否只需要构建一次? 

  3. 例如,ONNX Runtime 的原生 WebGPU 不支持在隔离网络中构建。因为该 EP 依赖于 Dawn,这很难处理。 

  4. 它今天有效,但 ONNX Runtime 有许多 EP 和依赖项。随着时间的推移,维护当前状态变得困难。