|  | def _mdbook_html_impl(ctx): | 
|  | # We will be generating our own book.toml based on this rule's configuration. | 
|  | # | 
|  | # We do this because: | 
|  | #  - The book.toml must contain a reference to the source files of the | 
|  | #    generated book. This only works as long as the source files are not | 
|  | #    generated by Bazel. | 
|  | #  - The book.toml file effectively describes a build, so it makes sense to | 
|  | #    port that over to be fully managed by the Bazel rule for mdbook. This | 
|  | #    makes things more consistent with the rest of our Bazel usage, at the | 
|  | #    expense of slightly deviating from how upstream does things. | 
|  | # | 
|  | # We emit the toml into `book.toml` because that's what mdbook needs. | 
|  | # However, instead of emitting it in a subdirectory named after this | 
|  | # target, we do that in `${target}_`. This is so that we can use the target | 
|  | # name as a directory containing the actual generated HTML files, making | 
|  | # the life of developers using this rule a bit easier. | 
|  | out_book_toml = ctx.actions.declare_file(ctx.attr.name + "_/book.toml") | 
|  |  | 
|  | # Find root of given handbook from srcs - there must be exactly one | 
|  | # SUMMARY.md and the parent directory of that is the root. | 
|  | summary = None | 
|  | for f in ctx.files.srcs: | 
|  | if not f.path.endswith('/SUMMARY.md'): | 
|  | continue | 
|  | if summary != None: | 
|  | fail("More then one SUMMARY.md provided.") | 
|  | summary = f | 
|  | if summary == None: | 
|  | fail("No SUMMARY.md provided in srcs.") | 
|  |  | 
|  | # We now have the SUMMARY.md file from which we can figure out the source | 
|  | # directory of the book. However, mdbook takes a source root path relative | 
|  | # to the book.toml file, not one relative to the current working | 
|  | # directory... Thus, we need to prepend a list of '../' elements that bring | 
|  | # mdbook down from the book.toml location back into the workspace execution | 
|  | # root, which is where our SUMMARY.md path is itself rooted. | 
|  | # | 
|  | # For example, if book.toml lives in: | 
|  | #   execroot/dev_source_monogon/bazel-out/k8-fastbuild/bin/metropolis/handbook/handbook_/book.toml | 
|  | # Then we will need to prepend: | 
|  | #   ../../../../../../../ | 
|  | # To get back to execroot/. | 
|  | prepend = len(out_book_toml.path.split('/')) - 1 | 
|  | src_dir_path = ('../' * prepend) + summary.dirname | 
|  |  | 
|  | # Generate book.toml. | 
|  | # Bazel does not have a toml library. We abuse JSON encoding to get | 
|  | # serialized list/string data as an acceptable substitute to building a | 
|  | # fully self-standing toml serializer for Bazel. | 
|  | book_toml_contents = [ | 
|  | "[book]", | 
|  | "title = {}".format(json.encode(ctx.attr.title)), | 
|  | "authors = {}".format(json.encode(ctx.attr.authors)), | 
|  | "language = {}".format(json.encode(ctx.attr.language)), | 
|  | "multilingual = false", | 
|  | "src = {}".format(json.encode(src_dir_path)), | 
|  | ] | 
|  | ctx.actions.write( | 
|  | output = out_book_toml, | 
|  | content = "\n".join(book_toml_contents) | 
|  | ) | 
|  |  | 
|  | out_dir = ctx.actions.declare_directory(ctx.attr.name) | 
|  | # We also have to prepend the out dir path, for the same reasons for which | 
|  | # we prepend src_dir_path above. | 
|  | out_dir_path = ('../' * prepend) + out_dir.path | 
|  | ctx.actions.run( | 
|  | executable = ctx.executable._mdbook, | 
|  | arguments = [ | 
|  | "build", | 
|  | "-d", out_dir_path, | 
|  | out_book_toml.dirname, | 
|  | ], | 
|  | inputs = ctx.files.srcs + [ out_book_toml ], | 
|  | outputs = [ out_dir ], | 
|  | ) | 
|  | return [ | 
|  | DefaultInfo( | 
|  | files = depset([out_dir]), | 
|  | ) | 
|  | ] | 
|  |  | 
|  | mdbook_html = rule( | 
|  | doc = "Build an mdbook source root into HTML files.", | 
|  | implementation = _mdbook_html_impl, | 
|  | attrs = { | 
|  | "title": attr.string( | 
|  | doc = "The title of the generated book.", | 
|  | ), | 
|  | "authors": attr.string_list( | 
|  | default = ["Monogon Project Authors"], | 
|  | doc = "The authors of the generated book.", | 
|  | ), | 
|  | "language": attr.string( | 
|  | default = "en", | 
|  | doc = "The language of the generated book.", | 
|  | ), | 
|  | "srcs": attr.label_list( | 
|  | allow_files = True, | 
|  | doc = "The sources of the generated book. Exaclty one file must be named SUMMARY.md, and that file's location will be used to determine the root of the book sources.", | 
|  | ), | 
|  |  | 
|  | "_mdbook": attr.label( | 
|  | doc = "The mdbook tool.", | 
|  | executable = True, | 
|  | cfg = "host", | 
|  | default = "@dev_source_monogon//third_party/rust:cargo_bin_mdbook", | 
|  | ), | 
|  | }, | 
|  | ) |