首页
/ Kong 中 PCRE2 的 Bazel 构建机制详解:从版本锁定到 nginx --with-pcre-jit 的完整依赖链

Kong 中 PCRE2 的 Bazel 构建机制详解:从版本锁定到 nginx --with-pcre-jit 的完整依赖链

2026-09-05 17:27:43作者:魏献源Searcher

本篇以 build/openresty/pcre/README.md 为核心,系统讲解 Kong 源码构建体系中 PCRE2 正则引擎的 Bazel 集成方案。读完本文,你将理解 Kong 如何从上游 rules_foreign_cc 示例改造出可复用的第三方依赖构建规则,如何在 .requirements 中锁定 PCRE2 版本与 SHA256 校验值,以及编译出的静态库如何被 OpenResty(nginx)通过 --with-pcre-jit 消费——这是理解 Kong 高性能路由匹配(正则路由)底层依赖构建的关键。

一、文档背景:Kong 为何要自己管理 PCRE 构建

build/openresty/pcre/README.md 虽然篇幅很短,但它记录了 Kong 构建体系中一个关键的工程决策:Kong 没有直接复用 Bazel 官方 rules_foreign_cc 项目中现成的第三方 C/C++ 库示例,而是基于该项目的示例做了针对性改造,改造点有两条:

  1. 版本号不再硬编码在构建规则里,而是从版本清单文件读取(README 中写作 requirements.txt,在本仓库实际落地为 build/.requirements);
  2. build_file 路径更新为 //build/openresty 目录下的新位置,即 build/openresty/pcre/BUILD.pcre.bazel

这个决策背后的原因是:Kong 的构建体系需要对 Nginx、LuaJIT、PCRE、OpenSSL、Brotli 等十余个底层依赖做统一、可复现的版本锁定。Kong 运行在 nginx(OpenResty)之上,nginx 的正则能力(location ~rewrite、路由匹配)完全依赖 PCRE 库。Kong 的 changelog 清晰地记录了这条依赖线的演进:

  • 3.7.0 将 PCRE 从遗留的 libpcre 8.45 升级到 libpcre2 10.43(见 bump-pcre.yml);
  • 3.8.0 继续升级到 PCRE2 10.44(见 bump-pcre.yml);
  • 当前未发布版本已升级到 PCRE2 10.45(见 bump-pcre.yml)。

PCRE2 相比 PCRE 1 是重大重写(API、内部结构均不兼容),Kong 选择跟随 nginx 上游完成迁移,而迁移能否在 x86_64/aarch64、Linux/macOS、原生/交叉编译等全矩阵下稳定复现,正是这套 Bazel 规则要解决的问题。

二、版本锁定:.requirements 如何成为唯一事实来源

README 提到的第一条改造——"从 requirements 文件读取版本"——在仓库中的实现链路是:

1. 版本清单文件 build/.requirements 中显式锁定:

PCRE=10.45
PCRE_SHA256=0e138387df7835d7403b8351e2226c1377da804e0737db0e071b48f07c9d12ee

2. 绑定仓库读取该文件build/kong_bindings.bzl 通过 repository_rule 在分析阶段读取 @kong//:.requirements(第 7 行 ctx.read(Label("@kong//:.requirements"))),解析每一行 KEY=VALUE 后生成 variables.bzl 文件,其中第 69 行写出 KONG_VAR = { ... } 字典。这样所有 Bazel 规则都能以 KONG_VAR["PCRE"]KONG_VAR["PCRE_SHA256"] 的形式引用版本,而无需知道文件细节。

3. 消费方build/openresty/pcre/pcre_repositories.bzl 第 8 行直接 version = KONG_VAR["PCRE"]

这种"单点锁定"设计的收益:升级 PCRE2 只需改 .requirements 两行(版本 + SHA256),配合 changelog 提交即可完成一次受控升级,且 SHA256 校验保证了供应链完整性。

三、源码获取:pcre_repositories 仓库规则

pcre_repositories.bzl 是一个标准的 http_archive 包装,核心逻辑(第 7–19 行):

def pcre_repositories():
    version = KONG_VAR["PCRE"]

    maybe(
        http_archive,
        name = "pcre",
        build_file = "//build/openresty/pcre:BUILD.pcre.bazel",
        strip_prefix = "pcre2-" + version,
        sha256 = KONG_VAR["PCRE_SHA256"],
        urls = [
            "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-" + version + "/pcre2-" + version + ".tar.gz",
        ],
    )

要点解析:

  • maybe 包装:确保该仓库规则在重复调用时幂等(openresty_repositories() 之外的其他入口也可能触发它);
  • build_file 参数:这正是 README 提到的第二条改造——把 BUILD.pcre.bazel 作为"外挂 BUILD 文件"注入下载并解压后的 PCRE2 源码树中,让一份没有 Bazel 支持的纯 C 项目立刻变成可构建的外部仓库(仓库标签为 @pcre);
  • strip_prefix = "pcre2-" + version:源码包顶层目录会随版本变化(pcre2-10.45/),用版本号动态拼接保证剥离前缀始终正确;
  • urls:指向 PCRE2 官方项目的 release 渠道,与 SHA256 成对使用。

该仓库规则由 build/openresty/repositories.bzl 中的 openresty_repositories() 统一编排,pcre_repositories() 是整个 OpenResty 依赖列表的第一个调用(第 33 行),随后依次是 OpenSSL、simdjson_ffi、atc_router、wasmx、brotli、snappy、ada 等,形成完整的底层依赖清单。

四、编译规则:BUILD.pcre.bazel 中的 cmake 目标

注入源码树的 BUILD.pcre.bazel 是整套规则的核心,它用 rules_foreign_cccmake 规则构建 PCRE2:

# pcre cmake detects cross compile automatically
cmake(
    name = "pcre",
    build_args = [
        "--",  # <- Pass remaining options to the native tool.
        "-j" + KONG_VAR["NPROC"],
    ],
    cache_entries = {
        "CMAKE_C_FLAGS": "${CMAKE_C_FLAGS:-} -fPIC",
        "PCRE2_SUPPORT_JIT": "ON",  # enable JIT support for pcre2_jit_compile
        "PCRE2_BUILD_PCRE2GREP": "OFF",  # we don't need the cli binary
        "PCRE2_BUILD_TESTS": "OFF",  # test doesn't compile on aarch64-linux-gnu (cross)
        "CMAKE_INSTALL_LIBDIR": "lib",  # force distros that uses lib64 (rhel family) to use lib
    },
    lib_source = ":all_srcs",
    out_static_libs = ["libpcre2-8.a"],
    visibility = ["//visibility:public"],
)

逐条说明各参数的工程含义:

参数 作用
PCRE2_SUPPORT_JIT: ON 启用 JIT 编译支持,生成 pcre2_jit_compile 能力。nginx 正则热点路径(路由匹配)可受益,且必须与 OpenResty 侧 --with-pcre-jit 配置配套(见第五节)
PCRE2_BUILD_PCRE2GREP: OFF 不构建 pcre2grep 命令行工具,网关只需要库
PCRE2_BUILD_TESTS: OFF 源码注释说明原因:PCRE2 自带测试在 aarch64-linux-gnu 交叉编译环境下无法编译,因此关闭以避免交叉构建失败
CMAKE_C_FLAGS: ... -fPIC 追加 -fPIC 位置无关代码标志,保证静态库可被动态链接进最终产物
CMAKE_INSTALL_LIBDIR: lib 强制安装到 lib 而非 lib64,解决 RHEL 系发行版默认 lib64 导致的目录不一致问题
build_args 中的 ---j NPROC -- 之后的参数透传给 cmake 底层构建工具(make/ninja),NPROC 同样来自 .requirements 绑定,实现并行编译
out_static_libs = ["libpcre2-8.a"] 声明产物为 8 位字符集的 PCRE2 静态库,供下游按名称消费

文件顶部的注释 # pcre cmake detects cross compile automatically 点出另一个设计事实:PCRE2 的 CMake 脚本能自动识别交叉编译目标(host ≠ target),因此 Kong 无需为 aarch64/x86_64 交叉场景额外传 CMAKE_SYSTEM_NAME 等变量,交叉构建的适配成本被上游工具链吸收。同目录的 BUILD.bazel 则用于声明本包内 .bzl/.bazel 文件在 Kong 主仓库侧的可见性。

五、消费端:OpenResty 如何把 PCRE 编进 nginx

PCRE 静态库最终被 build/openresty/BUILD.openresty.bazel 中的 configure_make 目标 openresty 消费,体现在三处:

1. configure 选项启用 JIT(第 127 行):

CONFIGURE_OPTIONS = [
    "--with-pcre-jit",
    ...

这与第四节 PCRE2_SUPPORT_JIT=ON 形成闭环:PCRE 库侧编译了 JIT 能力,nginx 侧再以 --with-pcre-jit 打开对应的运行时开关。

2. 头文件与库路径指向 @pcre 的产物(第 149、152 行):

"--with-cc-opt=\"-I$$EXT_BUILD_DEPS/pcre/include\"",
"--with-ld-opt=\"-L$$EXT_BUILD_DEPS/pcre/lib\"",

$$EXT_BUILD_DEPSrules_foreign_cc 提供的变量,指向依赖目标安装目录;pcre 子目录即上一节 cmake 规则 install 后的 include/ + lib/(其中 lib/ 正是 CMAKE_INSTALL_LIBDIR=lib 保证的目录)。同一组 --with-cc-opt/--with-ld-opt 也用于 OpenSSL 和 LuaJIT,体现了 Kong 对所有底层 C 依赖统一采用"外部静态库 + 路径注入"的集成模式。

3. 声明式依赖(第 327–330 行):

deps = [
    "@openresty//:luajit",
    "@openssl",
    "@pcre",
]

通过 deps 声明后,Bazel 会自动把 @pcre 加入执行顺序与远程缓存的 action key,保证 PCRE 先于 OpenResty 构建,且版本变化(.requirements 改动)会正确触发重编译。

六、构建与验证路径

从源码结构看,开发者可用的验证入口包括:

  • 单独构建 PCRE 目标:由于 BUILD.pcre.bazel 被注入到外部仓库,实际构建标签是 @pcre//:pcrecmake 规则名),产物为 libpcre2-8.a
  • 全链路构建@openresty//:openrestyconfigure_make 规则名,见 BUILD.openresty.bazel 第 273–349 行)会按依赖顺序先构建 @luajit@openssl@pcre,再执行 OpenResty 的 configure && make && make install
  • 调试辅助目标:同文件第 351 行起定义了 dev-just-make 生成规则,用于在已 configure 的构建目录中快速重跑 make && make install,适合修改 Nginx C 代码后的迭代验证。

完整的 Bazel 构建体系说明(包括交叉编译工具链、KONG_VAR 绑定机制等)可在 DEVELOPER.md 中继续查阅,其中"OpenResty system, including Nginx, LuaJIT, PCRE, etc."一节专门描述了该子系统的定位。PCRE 的许可文本也已按合规要求收录于仓库根目录的 COPYRIGHT 文件中。

七、小结

build/openresty/pcre/README.md 记录的是一次典型的"上游示例 → 生产化改造":Kong 在 rules_foreign_cc 官方示例基础上,把版本信息外置到 build/.requirements(当前锁定 PCRE2 10.45 并附 SHA256),把构建文件迁到 build/openresty/pcre/ 统一管理。围绕这份 README,整条链路是:

  1. kong_bindings.bzl 读取 .requirements 生成 KONG_VAR
  2. pcre_repositories.bzl 按版本拉取 PCRE2 源码并注入 BUILD.pcre.bazel
  3. cmake 规则以 JIT ON、关闭 pcre2grep 与测试、强制 lib 目录等参数产出 libpcre2-8.a
  4. BUILD.openresty.bazel 通过 --with-pcre-jit-I/-L $$EXT_BUILD_DEPS/pcre/...deps = ["@pcre"] 将其集成进 nginx 可执行文件。

这套机制让 Kong 在保持与 nginx/OpenResty 上游正则能力同步演进(PCRE 8 → PCRE2 10.x)的同时,获得了跨平台、可复现、带完整性校验的底层依赖构建能力。

登录后查看全文
热门项目推荐
相关项目推荐