Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions extensions/mdbook/private/mdbook.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,39 @@ def _rlocationpath(file, workspace_name):

return "{}/{}".format(workspace_name, file.short_path)

def _src_dest_path(file):
"""Returns the path of `file` inside the staged workdir, mirroring the build action's `_map_inputs`."""
dest = file.short_path
if dest.startswith("../"):
dest = "external/" + dest.removeprefix("../")
return dest

def _mdbook_server_impl(ctx):
toolchain = ctx.toolchains["@rules_rust_mdbook//:toolchain_type"]
book_info = ctx.attr.book[MdBookInfo]

args = ctx.actions.args()

args.add("--mdbook={}".format(_rlocationpath(toolchain.mdbook, ctx.workspace_name)))
args.add("--config={}".format(_rlocationpath(book_info.config, ctx.workspace_name)))
workspace_name = ctx.workspace_name

args.add("--mdbook={}".format(_rlocationpath(toolchain.mdbook, workspace_name)))
args.add("--config={}".format(_src_dest_path(book_info.config)))
args.add("--hostname={}".format(ctx.attr.hostname))
args.add("--port={}".format(ctx.attr.port))

workspace_name = ctx.workspace_name
def _src_map(file):
return "--src={}={}".format(_rlocationpath(file, workspace_name), _src_dest_path(file))

def _runfile_map(file):
# The set of files that must be staged into the workdir for `mdbook serve` to
# see a consistent source tree. `book.toml` is included so that referencing it
# by `--config` resolves to a real file rather than a runfiles symlink.
book_inputs = depset([book_info.config], transitive = [book_info.srcs])
args.add_all(book_inputs, map_each = _src_map, allow_closure = True)

def _plugin_map(file):
return "--plugin={}".format(_rlocationpath(file, workspace_name))

args.add_all(depset(transitive = [book_info.plugins, toolchain.plugins]), map_each = _runfile_map, allow_closure = True)
args.add_all(depset(transitive = [book_info.plugins, toolchain.plugins]), map_each = _plugin_map, allow_closure = True)

args_file = ctx.actions.declare_file("{}.mdbook_serve_args.txt".format(ctx.label.name))
ctx.actions.write(
Expand Down Expand Up @@ -167,7 +183,32 @@ def _mdbook_server_impl(ctx):

mdbook_server = rule(
implementation = _mdbook_server_impl,
doc = "Spawn an mdbook server for a given `mdbook` target.",
doc = """\
Spawn an mdbook server for a given `mdbook` target.

The server stages every input (including generated sources) into an isolated \
working directory before invoking `mdbook serve`, so the running book always \
reflects the bazel-built sources rather than the workspace checkout.

For live-reload during development, add `tags = ["ibazel_notify_changes"]` and \
invoke with [ibazel](https://github.com/bazelbuild/bazel-watcher):

```python
mdbook_server(
name = "book_server",
book = ":book",
tags = ["ibazel_notify_changes"],
)
```

```sh
ibazel run //path/to:book_server
```

ibazel will rebuild on source changes and signal the running server via stdin; \
the server re-stages the freshly built inputs and `mdbook serve` reloads any \
connected browsers.
""",
attrs = {
"book": attr.label(
doc = "The `mdbook` target to serve.",
Expand Down
Loading
Loading