PAYATHON 2026

如何在 Bazel 中解析 latex_document 构建警告并使构建失败?

数据小陈

结论

应将警告检查纳入 Bazel 的 action 依赖关系。可以在生成 PDF 的同一个 action 中解析日志,遇到指定警告时返回非零退出码;也可以将 .log 声明为输出,交给后续 action 检查。

原问题没有说明 latex_document 来自哪个规则库及其版本,因此无法确定它是否已经提供日志输出或检查钩子。具体接入点需要查看规则实现。

为什么不能直接读取 sandbox 中的日志?

在 sandbox 中看到 .log,只能说明编译工具生成了这个文件。后续 action 要使用日志,生成日志的 action 必须将它声明为输出,检查 action 也必须将它声明为输入。这是 Bazel 对 action 输入和输出的要求,见 Bazel 规则文档。

因此,在检查脚本中写入 sandbox 的绝对路径并不可靠。构建流程应通过声明的文件依赖找到日志。

如何接入 latex_document?

先检查规则的 .bzl 实现,确认日志是否已经通过默认输出、自定义 provider 或 output group 暴露出来。

如果可以修改编译 action,可以在它的执行脚本中增加以下步骤:

  1. 执行原有 LaTeX 编译流程,保留编译失败时的退出状态。
  2. 编译成功后,读取最终一轮编译产生的日志。
  3. 发现禁止的警告时打印诊断信息,并以非零状态退出。

这种方式在同一个 action 内读取日志,无需为警告检查单独导出 .log。

如果需要独立检查,应修改规则,将日志声明为输出,并确保编译命令把日志写入对应路径。随后让检查 action 依赖该日志。只在返回值里添加文件,却没有将它列入生成 action 的 outputs,无法完成接入。

独立检查的代码示例

下面假设规则已经暴露两个目标::raw_pdf 和 :latex_log,每个目标各包含一个文件。它们是示例标签,需要替换为项目实际提供的目标。

检查脚本 check_latex_log.py:

import re
import sys
from pathlib import Path

# 示例策略:将这两类诊断视为构建失败。
patterns = [
    re.compile(r"Overfull \\hbox"),
    re.compile(r"LaTeX Warning: There were undefined references\."),
]

log_path = Path(sys.argv[1])
lines = log_path.read_text(
    encoding="utf-8", errors="replace"
).splitlines()

hits = [
    line
    for line in lines
    if any(pattern.search(line) for pattern in patterns)
]

if hits:
    print("LaTeX warning check failed:", file=sys.stderr)
    for line in hits:
        print(line, file=sys.stderr)
    sys.exit(1)

在 Bazel 7.4 的 BUILD 文件中,可以用 py_binary 配合 genrule 接入。以下命令假设执行环境支持 POSIX shell 和 cp:

py_binary(
    name = "check_latex_log",
    srcs = ["check_latex_log.py"],
    main = "check_latex_log.py",
)

genrule(
    name = "checked_pdf",
    srcs = [
        ":raw_pdf",
        ":latex_log",
    ],
    tools = [":check_latex_log"],
    outs = ["document.checked.pdf"],
    cmd = "$(location :check_latex_log) " +
          "$(location :latex_log) && " +
          "cp $(location :raw_pdf) $@",
)

tools 用于声明检查工具,$(location ...) 获取声明文件的位置,$@ 表示该 genrule 的单个输出。相关语义见 genrule 文档。

构建时请求经过检查的目标:

bazel build //docs:checked_pdf

将 //docs 替换为实际包路径。命中警告时,检查程序返回 1,后续复制操作不会执行,构建随之失败。检查通过后,才会生成供后续流程使用的 PDF。

接入时需要注意什么?

  • CI 和发布流程应使用 checked_pdf,让检查成为必经依赖。直接构建原始 PDF 目标仍会绕过检查。
  • 检查最终一轮日志。如果构建流程会多次运行 LaTeX,中间轮次可能出现随后消失的交叉引用警告。
  • 按实际日志制定匹配规则。示例只匹配两种文本,不是完整的 LaTeX 日志解析器。跨行警告、宏包诊断和白名单需要另外处理。
  • 区分构建失败和测试失败。如果实现为测试目标,检查会在 bazel test 时运行;需要 bazel build 因警告失败时,应将检查放在构建所需的 action 链中。
  • 无法修改规则时,先确认已有的扩展能力。如果日志没有作为输出暴露,外层规则无法直接将 sandbox 中的临时日志变成可靠输入,通常需要扩展规则或调整其编译执行脚本。

备注:内容仅供参考。