tools/gostatic: init

This adds Bazel/hscloud integration to gostatic, via gostatic_tarball.

A sample is provided in //tools/gostatic/example, it can be built using:

    bazel build //tools/gostatic/example

The resulting tarball can then be extracted and viewed in a web
browser.

Change-Id: Idf8d4a8e0ee3a5ae07f7449a25909478c2d8b105
diff --git a/tools/gostatic/rules.bzl b/tools/gostatic/rules.bzl
new file mode 100644
index 0000000..e0326f9
--- /dev/null
+++ b/tools/gostatic/rules.bzl
@@ -0,0 +1,84 @@
+def _gostatic_tarball_impl(ctx):
+    out = ctx.actions.declare_directory("out")
+    tarball_name = ctx.attr.name + ".tar"
+    tarball = ctx.actions.declare_file(tarball_name)
+
+    config_name = ctx.attr.name + ".config"
+    config = ctx.actions.declare_file(config_name)
+
+    # Build path to root of sources, based on source_dir
+    # and location of the instantiating BUILDfile
+    # (source_dir is defined as relative to the BUILD file).
+    source_dir = '/'.join(ctx.build_file_path.split('/')[:-1]) + '/' + ctx.attr.source_dir
+
+    # Relative path to go up from generated config to build
+    # root. This is because gostatic is magical and really
+    # wants the config to be alongside the source.
+    up = "../" * (config.path.count("/"))
+    templates = " ".join([up + f.path for f in ctx.files.templates])
+    config_lines = [
+        "OUTPUT = {}".format(up + out.path),
+        "TEMPLATES = {}".format(templates),
+        "SOURCE = {}".format(up + source_dir),
+    ]
+    config_content = "\n".join(config_lines)
+    config_content += ctx.attr.extra_config
+
+    ctx.actions.write(config, config_content)
+
+    ctx.actions.run(
+        outputs = [out],
+        inputs = [config] + ctx.files.templates + ctx.files.srcs,
+        executable = ctx.file._gostatic,
+        arguments = [config.path],
+    )
+    ctx.actions.run(
+        outputs = [tarball],
+        inputs = [out],
+        executable = ctx.file._tarify,
+        arguments = [
+            "-site", out.path,
+            "-tarball", tarball.path,
+        ],
+    )
+
+    return [DefaultInfo(files=depset([tarball]))]
+
+gostatic_tarball = rule(
+    implementation = _gostatic_tarball_impl,
+    attrs = {
+        "extra_config": attr.string(
+            mandatory = True,
+            doc = """
+                Gostatic configuration (rules, etc). Do not specify OUTPUT, TEMPLATES
+                or SOURCES - these are automatically generated.
+            """,
+        ),
+        "source_dir": attr.string(
+            mandatory = True,
+            doc = "Root of site sources. Relative to BUILDfile.",
+        ),
+        "srcs": attr.label_list(
+            allow_files = True,
+            doc = "Site sources, all must be contained within source_dir"
+        ),
+        "templates": attr.label_list(
+            allow_files = True,
+            doc = "Templates to use (passed to TEMPLATES in gostatic config).",
+        ),
+        "_gostatic": attr.label(
+            default = Label("//tools/gostatic"),
+            allow_single_file = True,
+            executable = True,
+            cfg = "exec",
+            doc = "Path to gostatic binary.",
+        ),
+        "_tarify": attr.label(
+            default = Label("//tools/gostatic/tarify"),
+            allow_single_file = True,
+            executable = True,
+            cfg = "exec",
+            doc = "Path to tarify binary.",
+        ),
+    },
+)