diff options
Diffstat (limited to 'website/defs.bzl')
-rw-r--r-- | website/defs.bzl | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/website/defs.bzl b/website/defs.bzl new file mode 100644 index 000000000..fe711d5d1 --- /dev/null +++ b/website/defs.bzl @@ -0,0 +1,158 @@ +"""Wrappers for website documentation.""" + +# DocInfo is a provider which simple adds sufficient metadata to the source +# files (and additional data files) so that a jeyll header can be constructed +# dynamically. This is done the via BUILD system so that the plain +# documentation files can be viewable without non-compliant markdown headers. +DocInfo = provider( + fields = [ + "layout", + "description", + "permalink", + "category", + "subcategory", + "weight", + "editpath", + ], +) + +def _doc_impl(ctx): + return [ + DefaultInfo( + files = depset(ctx.files.src + ctx.files.data), + ), + DocInfo( + layout = ctx.attr.layout, + description = ctx.attr.description, + permalink = ctx.attr.permalink, + category = ctx.attr.category, + subcategory = ctx.attr.subcategory, + weight = ctx.attr.weight, + editpath = ctx.files.src[0].short_path, + ), + ] + +doc = rule( + implementation = _doc_impl, + doc = "Annotate a document for jekyll headers.", + attrs = { + "src": attr.label( + doc = "The markdown source file.", + mandatory = True, + allow_single_file = True, + ), + "data": attr.label_list( + doc = "Additional data files (e.g. images).", + allow_files = True, + ), + "layout": attr.string( + doc = "The document layout.", + default = "docs", + ), + "description": attr.string( + doc = "The document description.", + default = "", + ), + "permalink": attr.string( + doc = "The document permalink.", + mandatory = True, + ), + "category": attr.string( + doc = "The document category.", + default = "", + ), + "subcategory": attr.string( + doc = "The document subcategory.", + default = "", + ), + "weight": attr.string( + doc = "The document weight.", + default = "50", + ), + }, +) + +def _docs_impl(ctx): + # Tarball is the actual output. + tarball = ctx.actions.declare_file(ctx.label.name + ".tgz") + + # But we need an intermediate builder to translate the files. + builder = ctx.actions.declare_file("%s-builder" % ctx.label.name) + builder_content = [ + "#!/bin/bash", + "set -euo pipefail", + "declare -r T=$(mktemp -d)", + "function cleanup {", + " rm -rf $T", + "}", + "trap cleanup EXIT", + ] + for dep in ctx.attr.deps: + doc = dep[DocInfo] + + # Sanity check the permalink. + if not doc.permalink.endswith("/"): + fail("permalink %s for target %s should end with /" % ( + doc.permalink, + ctx.label.name, + )) + + # Construct the header. + header = """\ +description: {description} +permalink: {permalink} +category: {category} +subcategory: {subcategory} +weight: {weight} +editpath: {editpath} +layout: {layout}""" + + for f in dep.files.to_list(): + builder_content += ["echo Processing %s... >&2" % f.short_path] + builder_content += ["mkdir -p $T/$(dirname %s)" % f.short_path] + + # Is this a markdown file? If not, then we ensure that it ends up + # in the same path as the permalink for relative addressing. + if not f.basename.endswith(".md"): + builder_content += ["mkdir -p $T/%s" % doc.permalink] + builder_content += ["cp %s $T/%s" % (f.path, doc.permalink)] + continue + + # Construct the header dynamically. We include the title field from + # the markdown itself, as this is the g3doc format required. The + # title will be injected by the web layout however, so we don't + # want this to appear in the document. + args = dict([(k, getattr(doc, k)) for k in dir(doc)]) + builder_content += ["title=\"$(grep -E '^# ' %s | head -n 1 | cut -d'#' -f2-)\"" % f.path] + builder_content += ["cat >$T/%s <<EOF" % f.short_path] + builder_content += ["---"] + builder_content += ["title: $title"] + builder_content += [header.format(**args)] + builder_content += ["---"] + builder_content += ["EOF"] + builder_content += ["grep -v -E '^# ' %s >>$T/%s || true" % (f.path, f.short_path)] + + builder_content += ["declare -r filename=$(readlink -m %s)" % tarball.path] + builder_content += ["(cd $T && tar -zcf \"${filename}\" .)\n"] + ctx.actions.write(builder, "\n".join(builder_content), is_executable = True) + + # Generate the tarball. + ctx.actions.run( + inputs = depset(ctx.files.deps), + outputs = [tarball], + progress_message = "Generating %s" % ctx.label, + executable = builder, + ) + return [DefaultInfo( + files = depset([tarball]), + )] + +docs = rule( + implementation = _docs_impl, + doc = "Construct a site tarball from doc dependencies.", + attrs = { + "deps": attr.label_list( + doc = "All document dependencies.", + ), + }, +) |