bacon:替代 cargo-watch 的 Rust 监控工具实战指南(含 workspace / hook / 精准 watch)

Rust 生态里最常见的“文件变更自动跑命令”方案长期是 cargo-watch。但它的上游仓库已经被归档为只读(archived),不少团队开始寻找更活跃、更“工程化”的替代品。(GitHub)

bacon 的定位是“后台代码检查器”:把“要跑什么命令、监听哪些文件、失败/成功后做什么动作”都收敛到一套可维护的 jobs 配置里,适合单项目,也适合 workspace/monorepo。(GitHub)


1. 快速开始:安装与第一条命令

安装:

cargo install --locked bacon

进入项目根目录运行:

bacon

不带参数时会跑默认 job(通常是 check),并持续监听文件变化触发重跑。(Dystroy)

如果你想把配置放进仓库并共享给团队,可以在项目里初始化:

bacon --init

初始化会生成项目级 bacon.toml,官方也建议把它纳入版本控制,随着项目演进逐步调整。(GitHub)


2. bacon 的核心抽象:Jobs + 配置加载顺序

2.1 Job 是什么?

在 bacon 里,一切围绕 job

  • 一条命令(通常是 cargo check / cargo test / cargo run / clippy 等)
  • 一套 watch/ignore 策略(哪些文件变化触发、哪些忽略)
  • 可选的成功/失败 hook(例如失败播放提示音、成功切回上一个 job、导出诊断等)

仓库 README 的例子里就展示了用 job 自定义 targets/examples 等检查命令。(GitHub)

2.2 配置文件放哪?workspace 和 package 怎么覆盖?

bacon 会按顺序加载多层配置,后加载的覆盖先加载的(包括默认内置配置、全局 prefs、workspace/package 的 metadata 与 bacon.toml、环境变量指定配置、以及 --config-toml 注入内容等)。(Dystroy)

这对 monorepo 的意义是:

  • 根目录放 workspace 级 bacon.toml:定义“全局 job + 常用 crate job”
  • 特定 crate 需要差异化时,再在 crate 目录补一个更小的 bacon.toml(只写差异)

3. 你最关心的问题:怎么像 cargo watch -- --xx -xx 那样传参?

bacon cookbook 里有一条关键规则:

-- 之后的参数不会被 bacon 解析,会原样转发给 job 的命令。(Dystroy)

与此同时,Cargo 自己也有 -- 语义:cargo run-- 后面是传给二进制的参数;cargo test-- 后面是传给测试二进制/harness 的参数。(Rust 文档)

因此你会经常看到 --

3.1 传参给二进制(等价 cargo run -- --xx -xx

bacon run -- -- --xx -xx -xx
  • 第一个 --:告诉 bacon “后面都是 job 追加参数”
  • 第二个 --:告诉 cargo “后面都是二进制参数”(Dystroy)

3.2 同时传给 Cargo 与二进制

例如你要选择 package + 给二进制传参:

bacon run -- -p api -- --port 8080 --log debug

规则仍然是:Cargo 参数放在 Cargo 的 -- 之前,二进制参数放在 Cargo 的 -- 之后。(Rust 文档)

3.3 test 的情况(等价 cargo test -- --nocapture

bacon test -- -- --nocapture

同样是 “bacon 的 -- + cargo test 的 --”。(Massachusetts Institute of Technology)


4. 单项目(single crate)开发:最小可用到工程化配置

4.1 “开箱即用”的三件套

很多项目只需要这三条:

bacon check
bacon clippy
bacon test

你可以把它理解成 cargo watch -x check/-x clippy/-x test 的更结构化版本。

4.2 跑程序(run)并支持一次性参数

最典型的是本地调试:

bacon run -- -- --config local.toml --port 8080

无需改 bacon.toml,非常适合临时参数。(Dystroy)

4.3 长运行服务(server)自动重启:把“重启策略”写进 job

对 HTTP server / daemon 这类常驻进程,建议单独建一个 job,并明确“变更后先 kill 再重启”的策略(字段名与策略在官方配置文档里有说明)。(Docs.rs)

示例(放在项目 bacon.toml):

[jobs.server]
command = ["cargo", "run", "--bin", "server"]
need_stdout = true
background = false
on_change_strategy = "kill_then_restart"

如果你的程序需要更温柔的退出(例如希望 SIGINT 而不是强杀),可以继续加 kill = [...] 之类的自定义策略(同样属于 job 字段范畴)。(Docs.rs)


5. workspace / monorepo:单一 crate watch、多 crates watch 与折中方案

workspace 的难点不在“能不能跑”,而在“怎样避免无关 crate 的改动导致频繁触发”。

下面给三种常用模式,从“最推荐”到“最省心”。

5.1 推荐:根目录一个 bacon.toml,定义全局与常用 crate jobs

default_job = "check-all"

[jobs.check-all]
command = ["cargo", "check", "--workspace", "--all-targets"]

[jobs.test-all]
command = ["cargo", "test", "--workspace"]
need_stdout = true

[jobs.check-core]
command = ["cargo", "check", "-p", "core"]
default_watch = false
watch = ["crates/core/src", "crates/core/Cargo.toml"]

[jobs.check-api]
command = ["cargo", "check", "-p", "api"]
default_watch = false
watch = ["crates/api/src", "crates/api/Cargo.toml"]

关键点是:对“单 crate job”关闭默认 watch,然后只 watch 该 crate 的目录/清单文件,从源头降低噪音。相关字段(default_watch, watch 等)在配置文档中有完整定义。(Docs.rs)

5.2 更像“在 crate 目录里开发”:使用 workdir

如果你希望命令以某个 crate 目录为工作目录运行(例如相对路径更自然):

[jobs.core]
workdir = "crates/core"
command = ["cargo", "check"]

workdir 也是官方 job 字段的一部分。(Docs.rs)

5.3 同时 watch 多个 crate:多开 bacon 实例(KISS)

bacon 的 UI 通常聚焦一个 active job。要并行观察多条流水线的输出,最简单可靠的方法是:

  • 开两个 terminal tab
  • 每个 tab 跑一个 bacon <job>(例如 bacon check-core / bacon check-api
  • 或用 tmux 分屏

这往往比把“多任务并行”塞进单个 job 更稳定、更符合可维护性。


6. 精准 watch:特定文件、gitignore、ignore 组合与常见坑

6.1 只 watch 指定路径(强控触发范围)

[jobs.fast-check]
command = ["cargo", "check", "-p", "core"]
default_watch = false
watch = ["crates/core/src", "crates/core/Cargo.toml"]

6.2 想 watch .env 这类通常被 gitignore 的文件?

bacon 支持根据 gitignore 规则决定是否触发(apply_gitignore),这会导致“改了 .env 但没触发”的错觉。(Docs.rs)

解决办法通常是对该 job 关闭 gitignore 应用:

[jobs.api-server]
command = ["cargo", "run", "-p", "api", "--bin", "server"]
background = false
on_change_strategy = "kill_then_restart"

default_watch = false
watch = ["crates/api/src", ".env"]
apply_gitignore = false

补充背景:gitignore 的本意就是“告诉工具哪些文件不应被纳入版本控制”,很多工具会默认尊重它。(GitHub Docs)

6.3 忽略生成文件/快照文件/临时文件

ignore = [...](glob 列表)把噪音挡在外面:

[jobs.test]
command = ["cargo", "test"]
need_stdout = true
ignore = [
  ".snap.new",
  "crates/**/src/generated/*.rs",
]

7. Hook 与自动化:on_success/on_failure、避免循环、导出诊断

7.1 用 on_success/on_failure 实现 hook

bacon 支持在 job 成功或失败后触发动作(on_success / on_failure)。(Docs.rs)

但要注意:官方明确提醒不要写成互相触发的循环(A 成功触发 B、B 成功触发 A),否则会导致 job 一直跑不停。(Docs.rs)

7.2 locations export:把诊断输出给编辑器/脚本

如果你希望 IDE 或脚本读取诊断位置,可以使用 exports(例如 locations),把错误位置写到一个文件里供外部消费。这属于 bacon 的配置能力范畴。(Docs.rs)


8. 故障排查:三类问题一条路径解决

8.1 文件改了但没触发

按这个顺序查:

  1. 你是不是 default_watch = false 但忘记把路径加进 watch?(Docs.rs)
  2. 文件是否被 gitignore 覆盖,而该 job apply_gitignore = true?(Docs.rs)
  3. 是否被 ignore 的 glob 规则过滤?(Docs.rs)

8.2 参数传递不符合预期

记住这条心智模型:

  • bacon 的 --:从这里开始,后面参数原样追加到命令末尾(Dystroy)
  • Cargo 的 --:从这里开始,后面参数给二进制或测试 harness(Rust 文档)

所以 run/test 常见就是双 --

8.3 server 重启不干净

优先确认 job 里用了 on_change_strategy = "kill_then_restart",并按需要自定义 kill 行为。相关字段都在官方配置文档中。(Docs.rs)


9. 一份可以直接落地的 workspace 模板

把下面这个 bacon.toml 放到 workspace 根目录,先跑起来,再按你们真实目录结构微调:

default_job = "check-all"

[jobs.check-all]
command = ["cargo", "check", "--workspace", "--all-targets"]

[jobs.test-all]
command = ["cargo", "test", "--workspace"]
need_stdout = true

[jobs.check-core]
command = ["cargo", "check", "-p", "core"]
default_watch = false
watch = ["crates/core/src", "crates/core/Cargo.toml"]

[jobs.api-server]
command = ["cargo", "run", "-p", "api", "--bin", "server"]
need_stdout = true
background = false
on_change_strategy = "kill_then_restart"
default_watch = false
watch = ["crates/api/src", "crates/api/Cargo.toml", ".env"]
apply_gitignore = false

运行示例:

# 全 workspace check
bacon check-all

# 只盯 core crate
bacon check-core

# 启动 server 并给二进制传参(临时一次)
bacon api-server -- -- --port 8080 --log debug

参考资料

  • bacon 配置加载顺序与配置入口(官方站点)(Dystroy)
  • bacon cookbook:-- 后参数转发与“可能需要双 --”(Dystroy)
  • Cargo Book:cargo run 参数分隔规则(Rust 文档)
  • Cargo 测试参数分隔(cargo test -- ...)说明(Massachusetts Institute of Technology)
  • bacon GitHub README(jobs 示例与工程化建议)(GitHub)