1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2026-06-12 20:33:33 +02:00

bazel: migrate external mdbook plugins to Bazel

Configure mdbook-i18n-helpers, mdbook-linkcheck2, mdbook-pandoc, and
mdbook-svgbob as Bazel external plugins using `rules_rust`.

Update `xtask/src/main.rs` to build all preprocessor tools via Bazel,
programmatically resolve their paths, copy them to `~/.cargo/bin`.

This is a drop-in replacement for the old Cargo based approach. It
will go away as we move the `mdbook build` call itself to Bazel, but
it's useful in its own since it establishes that we can build the
`mdbook` plugins with Bazel.
This commit is contained in:
Martin Geisler
2026-06-09 19:47:23 +02:00
parent da2c12ea63
commit 7247cc34fb
3 changed files with 8706 additions and 25 deletions
+55
View File
@@ -13,6 +13,18 @@ use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
rust_host_tools = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools")
rust_host_tools.host_tools(
name = "rust_host_tools_nightly",
sha256s = {
"2025-09-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "9a701f2eb103703c018518066fa7deb476f7ebab548b1c4e2ea0df81ee42a20f",
"2025-09-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "b3c2d890d9405285e015ab4ceb78087e3ec55c64b0b3030d79102dfe5e622e09",
"2025-09-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "7f5486ae6ece8a734149e5fce6dc8f1bcdcb36b5c5cb53a819569109b445c700",
},
version = "nightly/2025-09-01",
)
use_repo(rust_host_tools, "rust_host_tools_nightly")
crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
crate.from_cargo(
name = "crates",
@@ -20,3 +32,46 @@ crate.from_cargo(
manifests = ["//:Cargo.toml"],
)
use_repo(crate, "crates")
# Repositories for mdbook plugins. Add new plugins to the
# `mdbook_plugins` repository. If this fails due to conflicts in their
# dependencies, isolate it below with additional repositories.
crate.spec(
artifact = "bin",
package = "mdbook-i18n-helpers",
repositories = ["mdbook_plugins"],
version = "0.3.6",
)
crate.spec(
artifact = "bin",
package = "mdbook-linkcheck2",
repositories = ["mdbook_plugins"],
version = "0.9.1",
)
crate.spec(
artifact = "bin",
package = "mdbook-pandoc",
repositories = ["mdbook_plugins"],
version = "0.10.4",
)
crate.from_specs(
name = "mdbook_plugins",
generate_binaries = True,
host_tools = "@rust_host_tools_nightly",
)
use_repo(crate, "mdbook_plugins")
# mdbook-svgbob depends transitively on sauron-core, which has a
# "=0.3.30" dependency on futures. This conflicts with other plugins.
crate.spec(
artifact = "bin",
package = "mdbook-svgbob",
repositories = ["svgbob_plugin"],
version = "0.2.2",
)
crate.from_specs(
name = "svgbob_plugin",
generate_binaries = True,
host_tools = "@rust_host_tools_nightly",
)
use_repo(crate, "svgbob_plugin")
+8589 -1
View File
File diff suppressed because one or more lines are too long
+62 -24
View File
@@ -139,33 +139,46 @@ fn install_tools(binstall: bool) -> Result<()> {
run_command(&mut cmd)?;
}
// The --locked flag is important for reproducible builds.
let tools = [
("mdbook", "0.4.52"),
("mdbook-svgbob", "0.2.2"),
("mdbook-pandoc", "0.10.4"),
("mdbook-i18n-helpers", "0.3.6"),
("i18n-report", "0.2.0"),
("mdbook-linkcheck2", "0.9.1"),
];
// Tools not yet migrated to be installed with Bazel.
let tools = [("mdbook", "0.4.52"), ("i18n-report", "0.2.0")];
// The --locked flag is important for reproducible builds.
for (tool, version) in tools {
let mut cmd = Command::new(cargo);
cmd.args([install_command, tool, "--version", version, "--locked"]);
run_command(&mut cmd)?;
}
// Install local tools from the workspace by building them with
// Bazel.
// Install local and external tools from the workspace by building
// them with Bazel.
let workspace_dir = Path::new(env!("CARGO_WORKSPACE_DIR"));
let bazel_tools = [
("//mdbook-course", "mdbook-course"),
("//mdbook-exerciser", "mdbook-exerciser"),
("@mdbook_plugins//:mdbook-i18n-helpers__mdbook-gettext", "mdbook-gettext"),
(
"@mdbook_plugins//:mdbook-i18n-helpers__mdbook-xgettext",
"mdbook-xgettext",
),
("@mdbook_plugins//:mdbook-pandoc__mdbook-pandoc", "mdbook-pandoc"),
("@svgbob_plugin//:mdbook-svgbob__mdbook-svgbob", "mdbook-svgbob"),
(
"@mdbook_plugins//:mdbook-linkcheck2__mdbook-linkcheck2",
"mdbook-linkcheck2",
),
];
let mut cmd = Command::new("bazel");
cmd.current_dir(workspace_dir).args([
"build",
"//mdbook-course",
"//mdbook-exerciser",
]);
cmd.arg("build");
for (target, _) in &bazel_tools {
cmd.arg(target);
}
cmd.current_dir(workspace_dir);
run_command(&mut cmd)?;
// Copy compiled tools to ~/.cargo/bin, the same way `cargo
// install` would.
let cargo_home = match env::var("CARGO_HOME") {
Ok(cargo_home) => PathBuf::from(cargo_home),
Err(_) => {
@@ -174,14 +187,21 @@ fn install_tools(binstall: bool) -> Result<()> {
};
let bin_dir = cargo_home.join("bin");
fs::copy(
workspace_dir.join("bazel-bin/mdbook-course/mdbook-course"),
bin_dir.join("mdbook-course"),
)?;
fs::copy(
workspace_dir.join("bazel-bin/mdbook-exerciser/mdbook-exerciser"),
bin_dir.join("mdbook-exerciser"),
)?;
for (target, name) in bazel_tools {
let src = get_bazel_output_path(target)?;
let dest = bin_dir.join(name);
// Bazel creates read-only files, which block subsequent runs.
// We fix this by deleting the destination file, if it exists.
if let Err(e) = fs::remove_file(&dest)
&& e.kind() != std::io::ErrorKind::NotFound
{
return Err(anyhow!("Failed to remove {}: {}", dest.display(), e));
}
fs::copy(&src, &dest).with_context(|| {
format!("Failed to copy {} to {}", src.display(), dest.display())
})?;
}
// Uninstall original linkcheck if currently installed (see issue no 2773)
uninstall_mdbook_linkcheck()?;
@@ -402,3 +422,21 @@ fn get_output_dir(language: Option<String>, output_arg: Option<PathBuf>) -> Path
Path::new("book").join(language.unwrap_or("".to_string()))
}
}
/// Queries Bazel to find the output file path for a given target.
fn get_bazel_output_path(target: &str) -> Result<PathBuf> {
let output = Command::new("bazel")
.args(["cquery", "--output=files", target])
.output()
.with_context(|| format!("Failed to run bazel cquery for {target}"))?;
if !output.status.success() {
return Err(anyhow!(
"bazel cquery failed: {}",
String::from_utf8_lossy(&output.stderr)
));
}
let stdout = String::from_utf8(output.stdout)?;
let relative_path = stdout.trim();
let workspace_dir = Path::new(env!("CARGO_WORKSPACE_DIR"));
Ok(workspace_dir.join(relative_path))
}