From fd9d6b3d0791d57d270631c3072ec4f42b980bd7 Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Sat, 9 May 2026 11:13:42 +0200 Subject: [PATCH] mdbook: fix support for external repositories (#4026) When any part of an mdbook target (either the book.toml or the source files) comes from an external repository, the build was failing because the rule's staging logic and its invocation logic handled repository boundaries inconsistently. This change: - Updates _map_inputs in mdbook.bzl to map external repository paths to 'external/' instead of '../'. - Hardens the process wrapper with a bounds check to ensure files are not staged outside the shadow work directory. - Adds an integration test to verify the fix and prevent regressions. --- extensions/mdbook/private/mdbook.bzl | 10 +++++- extensions/mdbook/private/process_wrapper.rs | 11 ++++++ .../mdbook/test/external_srcs/BUILD.bazel | 35 +++++++++++++++++++ .../test/external_srcs/content/BUILD.bazel | 7 ++++ .../test/external_srcs/content/book.toml | 5 +++ .../test/external_srcs/content/src/SUMMARY.md | 3 ++ .../test/external_srcs/content/src/test.md | 3 ++ .../test/external_srcs/external_srcs_test.rs | 24 +++++++++++++ .../local_book_mixed/BUILD.bazel | 1 + .../external_srcs/local_book_mixed/book.toml | 5 +++ 10 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 extensions/mdbook/test/external_srcs/BUILD.bazel create mode 100644 extensions/mdbook/test/external_srcs/content/BUILD.bazel create mode 100644 extensions/mdbook/test/external_srcs/content/book.toml create mode 100644 extensions/mdbook/test/external_srcs/content/src/SUMMARY.md create mode 100644 extensions/mdbook/test/external_srcs/content/src/test.md create mode 100644 extensions/mdbook/test/external_srcs/external_srcs_test.rs create mode 100644 extensions/mdbook/test/external_srcs/local_book_mixed/BUILD.bazel create mode 100644 extensions/mdbook/test/external_srcs/local_book_mixed/book.toml diff --git a/extensions/mdbook/private/mdbook.bzl b/extensions/mdbook/private/mdbook.bzl index be30517257..d4c63d5219 100644 --- a/extensions/mdbook/private/mdbook.bzl +++ b/extensions/mdbook/private/mdbook.bzl @@ -10,7 +10,15 @@ MdBookInfo = provider( ) def _map_inputs(file): - return "{}={}".format(file.path, file.short_path) + dest = file.short_path + if dest.startswith("../"): + # External repositories have short_paths starting with '../'. + # We need them to be staged at 'external/' within our shadow + # directory to match how 'file.path' (and thus 'file.dirname') + # refers to them. + dest = "external/" + dest.removeprefix("../") + + return "{}={}".format(file.path, dest) def _mdbook_impl(ctx): output = ctx.actions.declare_directory(ctx.label.name) diff --git a/extensions/mdbook/private/process_wrapper.rs b/extensions/mdbook/private/process_wrapper.rs index 99a8663ca6..c354c57f10 100644 --- a/extensions/mdbook/private/process_wrapper.rs +++ b/extensions/mdbook/private/process_wrapper.rs @@ -73,6 +73,17 @@ fn generate_work_dir(output_dir: &Path, inputs_manifest: &BTreeMap {} (dest: {})", + src.display(), + abs_dest.display(), + dest.display() + ); + } + fs::create_dir_all(abs_dest.parent().unwrap()).unwrap(); fs::copy(src, &abs_dest).unwrap_or_else(|e| { panic!( diff --git a/extensions/mdbook/test/external_srcs/BUILD.bazel b/extensions/mdbook/test/external_srcs/BUILD.bazel new file mode 100644 index 0000000000..0772240aa9 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/BUILD.bazel @@ -0,0 +1,35 @@ +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("@rules_rust//rust:defs.bzl", "rust_test") +load("//:defs.bzl", "mdbook") + +mdbook( + name = "external_srcs", + srcs = ["//test/external_srcs/content:srcs"], + book = "//test/external_srcs/content:book.toml", +) + +mdbook( + name = "local_book_mixed", + srcs = ["//test/external_srcs/content:srcs"], + book = "//test/external_srcs/local_book_mixed:book.toml", +) + +rust_test( + name = "external_srcs_test", + srcs = ["external_srcs_test.rs"], + data = [ + ":external_srcs", + ":local_book_mixed", + ], + edition = "2021", + rustc_env = { + "MDBOOK_EXTERNAL_SRCS_OUTPUT": "$(rlocationpath :external_srcs)", + "MDBOOK_LOCAL_BOOK_MIXED_OUTPUT": "$(rlocationpath :local_book_mixed)", + }, + deps = ["@rules_rust//rust/runfiles"], +) + +build_test( + name = "external_srcs_build_test", + targets = [":external_srcs"], +) diff --git a/extensions/mdbook/test/external_srcs/content/BUILD.bazel b/extensions/mdbook/test/external_srcs/content/BUILD.bazel new file mode 100644 index 0000000000..0f1c9bd6d8 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/content/BUILD.bazel @@ -0,0 +1,7 @@ +exports_files(["book.toml"]) + +filegroup( + name = "srcs", + srcs = glob(["src/**/*.md"]), + visibility = ["//visibility:public"], +) diff --git a/extensions/mdbook/test/external_srcs/content/book.toml b/extensions/mdbook/test/external_srcs/content/book.toml new file mode 100644 index 0000000000..c2613930e5 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/content/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Test Author"] +language = "en" +src = "src" +title = "External Book" diff --git a/extensions/mdbook/test/external_srcs/content/src/SUMMARY.md b/extensions/mdbook/test/external_srcs/content/src/SUMMARY.md new file mode 100644 index 0000000000..57727bbfe9 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/content/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +[Test Chapter](test.md) diff --git a/extensions/mdbook/test/external_srcs/content/src/test.md b/extensions/mdbook/test/external_srcs/content/src/test.md new file mode 100644 index 0000000000..8fa9231dd1 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/content/src/test.md @@ -0,0 +1,3 @@ +# Test Chapter + +This is a test. diff --git a/extensions/mdbook/test/external_srcs/external_srcs_test.rs b/extensions/mdbook/test/external_srcs/external_srcs_test.rs new file mode 100644 index 0000000000..0dc3df4c67 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/external_srcs_test.rs @@ -0,0 +1,24 @@ +use runfiles::{rlocation, Runfiles}; +use std::fs; + +#[test] +fn test_external_srcs_output() { + let r = Runfiles::create().unwrap(); + + let dir = rlocation!(r, env!("MDBOOK_EXTERNAL_SRCS_OUTPUT")).unwrap(); + + let test_chapter = dir.join("test.html"); + let content = fs::read_to_string(test_chapter).unwrap(); + assert!(content.contains("This is a test.")); +} + +#[test] +fn test_local_book_mixed_output() { + let r = Runfiles::create().unwrap(); + + let dir = rlocation!(r, env!("MDBOOK_LOCAL_BOOK_MIXED_OUTPUT")).unwrap(); + + let test_chapter = dir.join("test.html"); + let content = fs::read_to_string(test_chapter).unwrap(); + assert!(content.contains("This is a test.")); +} diff --git a/extensions/mdbook/test/external_srcs/local_book_mixed/BUILD.bazel b/extensions/mdbook/test/external_srcs/local_book_mixed/BUILD.bazel new file mode 100644 index 0000000000..20015d57f6 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/local_book_mixed/BUILD.bazel @@ -0,0 +1 @@ +exports_files(["book.toml"]) diff --git a/extensions/mdbook/test/external_srcs/local_book_mixed/book.toml b/extensions/mdbook/test/external_srcs/local_book_mixed/book.toml new file mode 100644 index 0000000000..7d70307f36 --- /dev/null +++ b/extensions/mdbook/test/external_srcs/local_book_mixed/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Test Author"] +language = "en" +src = "../content/src" +title = "Local Book Mixed"