aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar David Chen <dzc@google.com>2015-09-12 01:32:26 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-09-14 15:39:18 +0000
commit361d2e2f7448038355b337ee4f7191a28c20755a (patch)
tree705fb5e52a6a36300d11cf2c0f451f2b9324ae7b /tools
parent2ad1c81755063b72a599844e730e0344344f61e8 (diff)
Add Jsonnet rules to Bazel.
RELNOTES: Add Jsonnet rules to Bazel -- MOS_MIGRATED_REVID=102895524
Diffstat (limited to 'tools')
-rw-r--r--tools/BUILD1
-rw-r--r--tools/build_defs/jsonnet/BUILD16
-rw-r--r--tools/build_defs/jsonnet/README.md310
-rw-r--r--tools/build_defs/jsonnet/jsonnet.BUILD80
-rw-r--r--tools/build_defs/jsonnet/jsonnet.WORKSPACE6
-rw-r--r--tools/build_defs/jsonnet/jsonnet.bzl139
6 files changed, 552 insertions, 0 deletions
diff --git a/tools/BUILD b/tools/BUILD
index a2df39179c..f370eb3049 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -23,6 +23,7 @@ filegroup(
name = "package-srcs",
srcs = glob(["**"]) + [
"//tools/build_defs/d:srcs",
+ "//tools/build_defs/jsonnet:srcs",
"//tools/build_defs/docker:srcs",
"//tools/build_rules/appengine:srcs",
"//tools/build_rules/closure:srcs",
diff --git a/tools/build_defs/jsonnet/BUILD b/tools/build_defs/jsonnet/BUILD
new file mode 100644
index 0000000000..7f38b97020
--- /dev/null
+++ b/tools/build_defs/jsonnet/BUILD
@@ -0,0 +1,16 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+)
+
+filegroup(
+ name = "jsonnet",
+ srcs = ["@jsonnet//:jsonnet"],
+)
+
+filegroup(
+ name = "std",
+ srcs = ["@jsonnet//:std"],
+)
diff --git a/tools/build_defs/jsonnet/README.md b/tools/build_defs/jsonnet/README.md
new file mode 100644
index 0000000000..6d40df7a61
--- /dev/null
+++ b/tools/build_defs/jsonnet/README.md
@@ -0,0 +1,310 @@
+# Jsonnet Rules
+
+## Rules
+
+* [`jsonnet_library`](#jsonnet_library)
+* [`jsonnet_to_json`](#jsonnet_to_json)
+
+## Overview
+
+These are build rules for working with [Jsonnet][jsonnet] files with Bazel.
+
+[jsonnet]: http://google.github.io/jsonnet/doc/
+
+## Setup
+
+To use the Jsonnet rules, simply copy the contents of `jsonnet.WORKSPACE` into
+your `WORKSPACE` file.
+
+<a name="#jsonnet_library"></a>
+## jsonnet_library
+
+```python
+jsonnet_library(name, srcs, deps, imports)
+```
+
+<table>
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>name</code></td>
+ <td>
+ <code>Name, required</code>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>srcs</code></td>
+ <td>
+ <code>List of Labels, required</code>
+ <p>
+ List of <code>.jsonnet</code> files that comprises this Jsonnet
+ library.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>deps</code></td>
+ <td>
+ <code>List of labels, optional</code>
+ <p>
+ List of targets that are required by the <code>srcs</code> Jsonnet
+ files.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>imports</code></td>
+ <td>
+ <code>List of strings, optional</code>
+ <p>
+ List of import <code>-J</code> flags to be passed to the
+ <code>jsonnet</code> compiler.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+### Example
+
+Suppose you have the following directory structure:
+
+```
+[workspace]/
+ WORKSPACE
+ configs/
+ BUILD
+ backend.jsonnet
+ frontend.jsonnet
+```
+
+You can use the `jsonnet_library` rule to build a collection of `.jsonnet`
+files that can be imported by other `.jsonnet` files as dependencies:
+
+`configs/BUILD`:
+
+```python
+load("/tools/build_defs/jsonnet/jsonnet", "jsonnet_library")
+
+jsonnet_library(
+ name = "configs",
+ srcs = [
+ "backend.jsonnet",
+ "frontend.jsonnet",
+ ],
+)
+```
+
+<a name="#jsonnet_to_json"></a>
+## jsonnet_to_json
+
+```python
+jsonnet_to_json(name, src, deps, outs, multiple_outputs, imports)
+```
+
+<table>
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>name</code></td>
+ <td>
+ <code>Name, required</code>
+ <p>A unique name for this rule.</p>
+ <p>
+ This name will be used as the name of the JSON file generated by this
+ rule.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>src</code></td>
+ <td>
+ <code>Label, required</code>
+ <p>
+ The <code>.jsonnet</code> file to convert to JSON.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>deps</code></td>
+ <td>
+ <code>List of labels, optional</code>
+ <p>
+ List of targets that are required by the <code>src</code> Jsonnet
+ file.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>outs</code></td>
+ <td>
+ <code>List of Filenames, optional</code>
+ <p>
+ Names of the output .json files to be generated by this rule.
+ </p>
+ <p>
+ If you are generating only a single JSON file and are not using
+ jsonnet multiple output files, then this attribute should only
+ contain the file name of the JSON file you are generating.
+ </p>
+ <p>
+ If you are generating multiple JSON files using jsonnet multiple file
+ output (<code>jsonnet -m</code>), then list the file names of all the
+ JSON files to be generated. The file names specified here must match
+ the file names specified in your <code>src</code> Jsonnet file.
+ </p>
+ <p>
+ For the case where multiple file output is used but only for
+ generating one output file, set the <code>multiple_outputs</code>
+ attribute to 1 to explicitly enable the <code>-m</code> flag for
+ multiple file output.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>multiple_outputs</code></td>
+ <td>
+ <code>bool, optional, default 0</code>
+ <p>
+ Set to 1 to explicitly enable multiple file output via the
+ <code>jsonnet -m</code> flag.
+ </p>
+ <p>
+ This is used for the case where multiple file output is used but only
+ for generating a single output file. For example:
+ </p>
+<pre>
+local foo = import "foo.jsonnet";
+
+{
+ "foo.json": foo,
+}
+</pre>
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>imports</code></td>
+ <td>
+ <code>List of strings, optional</code>
+ <p>
+ List of import <code>-J</code> flags to be passed to the
+ <code>jsonnet</code> compiler.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+### Example
+
+Suppose you have the following directory structure:
+
+```
+[workspace]/
+ WORKSPACE
+ workflows/
+ BUILD
+ workflow.jsonnet
+ wordcount.jsonnet
+ intersection.jsonnet
+```
+
+Say that `workflow.jsonnet` is a base configuration library for a workflow
+scheduling system and `wordcount.jsonnet` and `intersection.jsonnet` both
+import `workflow.jsonnet` to define workflows for performing a wordcount and
+intersection of two files, respectively.
+
+First, create a `jsonnet_library` target with `workflow.jsonnet`:
+
+`workflows/BUILD`:
+
+```python
+load("/tools/build_defs/jsonnet/jsonnet", "jsonnet_library")
+
+jsonnet_library(
+ name = "workflow",
+ srcs = ["workflow.jsonnet"],
+)
+```
+
+To compile `wordcount.jsonnet` and `intersection.jsonnet` to JSON, define two
+`jsonnet_to_json` targets:
+
+```python
+jsonnet_to_json(
+ name = "wordcount",
+ src = "wordcount.jsonnet",
+ outs = ["wordcount.json"],
+ deps = [":workflow"],
+)
+
+jsonnet_to_json(
+ name = "intersection",
+ src = "intersection.jsonnet",
+ outs = ["intersection.json"],
+ deps = [":workflow"],
+)
+```
+
+### Example: Multiple output files
+
+To use Jsonnet's [multiple output files][multiple-output-files], suppose you
+add a file `shell-workflows.jsonnet` that imports `wordcount.jsonnet` and
+`intersection.jsonnet`:
+
+`workflows/shell-workflows.jsonnet`:
+
+```
+local wordcount = import "workflows/wordcount.jsonnet";
+local intersection = import "workflows/intersection.jsonnet";
+
+{
+ "wordcount-workflow.json": wordcount,
+ "intersection-workflow.json": intersection,
+}
+```
+
+To compile `shell-workflows.jsonnet` into the two JSON files,
+`wordcount-workflow.json` and `intersection-workflow.json`, first create a
+`jsonnet_library` target containing the two files that
+`shell-workflows.jsonnet` depends on:
+
+```python
+jsonnet_library(
+ name = "shell-workflows-lib",
+ srcs = [
+ "wordcount.jsonnet",
+ "intersection.jsonnet",
+ ],
+ deps = [":workflow"],
+)
+```
+
+Then, create a `jsonnet_to_json` target and set `outs` to the list of output
+files to indicate that multiple output JSON files are generated:
+
+```python
+jsonnet_to_json(
+ name = "shell-workflows",
+ src = "shell-workflows.jsonnet",
+ deps = [":shell-workflows-lib"],
+ outs = [
+ "wordcount-workflow.jsonnet",
+ "intersection-workflow.jsonnet",
+ ],
+)
+```
+
+[multiple-output-files]: http://google.github.io/jsonnet/doc/commandline.html
diff --git a/tools/build_defs/jsonnet/jsonnet.BUILD b/tools/build_defs/jsonnet/jsonnet.BUILD
new file mode 100644
index 0000000000..4b7dca6ea6
--- /dev/null
+++ b/tools/build_defs/jsonnet/jsonnet.BUILD
@@ -0,0 +1,80 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "std",
+ srcs = ["std.jsonnet"],
+)
+
+genrule(
+ name = "gen-std-jsonnet-h",
+ srcs = ["std.jsonnet"],
+ outs = ["std.jsonnet.h"],
+ cmd = "((od -v -Anone -t u1 $< | tr \" \" \"\n\" | grep -v \"^$$\" " +
+ "| tr \"\n\" \",\" ) && echo \"0\") > $@; " +
+ "echo >> $@",
+)
+
+cc_library(
+ name = "jsonnet-common",
+ srcs = [
+ "lexer.cpp",
+ "parser.cpp",
+ "static_analysis.cpp",
+ "vm.cpp",
+ "std.jsonnet.h",
+ ],
+ hdrs = [
+ "lexer.h",
+ "parser.h",
+ "static_analysis.h",
+ "static_error.h",
+ "vm.h",
+ ],
+ linkopts = ["-lm"],
+ includes = ["."],
+)
+
+cc_library(
+ name = "libjsonnet",
+ srcs = ["libjsonnet.cpp"],
+ hdrs = ["libjsonnet.h"],
+ deps = [":jsonnet-common"],
+ includes = ["."],
+)
+
+cc_binary(
+ name = "jsonnet",
+ srcs = ["jsonnet.cpp"],
+ deps = [":libjsonnet"],
+ includes = ["."],
+)
+
+cc_binary(
+ name = "libjsonnet_test_snippet",
+ srcs = ["libjsonnet_test_snippet.c"],
+ deps = [":libjsonnet"],
+ includes = ["."],
+)
+
+cc_binary(
+ name = "libjsonnet_test_file",
+ srcs = ["libjsonnet_test_file.c"],
+ deps = [":libjsonnet"],
+ includes = ["."],
+)
+
+filegroup(
+ name = "object_jsonnet",
+ srcs = ["test_suite/object.jsonnet"],
+)
+
+sh_test(
+ name = "libjsonnet_test",
+ srcs = ["libjsonnet_test.sh"],
+ data = [
+ ":jsonnet",
+ ":libjsonnet_test_snippet",
+ ":libjsonnet_test_file",
+ ":object_jsonnet",
+ ],
+)
diff --git a/tools/build_defs/jsonnet/jsonnet.WORKSPACE b/tools/build_defs/jsonnet/jsonnet.WORKSPACE
new file mode 100644
index 0000000000..e6da943f34
--- /dev/null
+++ b/tools/build_defs/jsonnet/jsonnet.WORKSPACE
@@ -0,0 +1,6 @@
+new_git_repository(
+ name = "jsonnet",
+ remote = "https://github.com/google/jsonnet.git",
+ tag = "v0.8.0",
+ build_file = "tools/build_defs/jsonnet/jsonnet.BUILD",
+)
diff --git a/tools/build_defs/jsonnet/jsonnet.bzl b/tools/build_defs/jsonnet/jsonnet.bzl
new file mode 100644
index 0000000000..13a1ba54ec
--- /dev/null
+++ b/tools/build_defs/jsonnet/jsonnet.bzl
@@ -0,0 +1,139 @@
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Jsonnet rules for Bazel."""
+
+JSONNET_FILETYPE = FileType([".jsonnet"])
+
+def _setup_deps(deps):
+ """Collects source files and import flags of transitive dependencies.
+
+ Args:
+ deps: List of deps labels from ctx.attr.deps.
+
+ Returns:
+ Returns a struct containing the following fields:
+ transitive_sources: List of Files containing sources of transitive
+ dependencies
+ imports: List of Strings containing import flags set by transitive
+ dependency targets.
+ """
+ transitive_sources = set(order="compile")
+ imports = set()
+ for dep in deps:
+ transitive_sources += dep.transitive_jsonnet_files
+ imports += dep.imports
+
+ return struct(
+ transitive_sources = transitive_sources,
+ imports = imports)
+
+def _jsonnet_library_impl(ctx):
+ """Implementation of the jsonnet_library rule."""
+ depinfo = _setup_deps(ctx.attr.deps)
+ sources = depinfo.transitive_sources + ctx.files.srcs
+ imports = depinfo.imports + ctx.attr.imports
+ return struct(files = set(),
+ transitive_jsonnet_files = sources,
+ imports = imports)
+
+def _jsonnet_toolchain(ctx):
+ return struct(
+ jsonnet_path = ctx.file._jsonnet.path,
+ imports = ["-J %s" % ctx.file._std.dirname])
+
+def _jsonnet_to_json_impl(ctx):
+ """Implementation of the jsonnet_to_json rule."""
+ depinfo = _setup_deps(ctx.attr.deps)
+ toolchain = _jsonnet_toolchain(ctx)
+ command = (
+ [
+ "set -e;",
+ toolchain.jsonnet_path,
+ ] +
+ toolchain.imports +
+ ctx.attr.imports +
+ list(depinfo.imports) +
+ ["-J ."])
+
+ outputs = []
+ # If multiple_outputs is set to true, then jsonnet will be invoked with the
+ # -m flag for multiple outputs. Otherwise, jsonnet will write the resulting
+ # JSON to stdout, which is redirected into a single JSON output file.
+ if len(ctx.attr.outs) > 1 or ctx.attr.multiple_outputs:
+ output_json_files = [ctx.new_file(ctx.configuration.bin_dir, out.name)
+ for out in ctx.attr.outs]
+ outputs += output_json_files
+ command += ["-m", ctx.file.src.path]
+ # Currently, jsonnet -m creates the output files in the current working
+ # directory. Append mv commands to move the output files into their
+ # correct output directories.
+ # TODO(dzc): Remove this hack when jsonnet supports a flag for setting
+ # an output directory.
+ for json_file in output_json_files:
+ command += ["; mv %s %s" % (json_file.basename, json_file.path)]
+ else:
+ if len(ctx.attr.outs) > 1:
+ fail("Only one file can be specified in outs if multiple_outputs is " +
+ "not set.")
+
+ compiled_json = ctx.new_file(ctx.configuration.bin_dir,
+ ctx.attr.outs[0].name)
+ outputs += [compiled_json]
+ command += [ctx.file.src.path, "> %s" % compiled_json.path]
+
+ compile_inputs = (
+ [ctx.file.src, ctx.file._jsonnet, ctx.file._std] +
+ list(depinfo.transitive_sources))
+
+ ctx.action(
+ inputs = compile_inputs,
+ outputs = outputs,
+ mnemonic = "Jsonnet",
+ command = " ".join(command),
+ use_default_shell_env = True,
+ progress_message = "Compiling Jsonnet to JSON for " + ctx.label.name);
+
+_jsonnet_common_attrs = {
+ "deps": attr.label_list(providers = ["transitive_jsonnet_files"],
+ allow_files = False),
+ "imports": attr.string_list(),
+ "_jsonnet": attr.label(
+ default = Label("//tools/build_defs/jsonnet:jsonnet"),
+ executable = True,
+ single_file = True),
+ "_std": attr.label(default = Label("//tools/build_defs/jsonnet:std"),
+ single_file = True),
+}
+
+_jsonnet_library_attrs = {
+ "srcs": attr.label_list(allow_files = JSONNET_FILETYPE),
+}
+
+jsonnet_library = rule(
+ _jsonnet_library_impl,
+ attrs = _jsonnet_library_attrs + _jsonnet_common_attrs,
+)
+
+_jsonnet_to_json_attrs = {
+ "src": attr.label(allow_files = JSONNET_FILETYPE,
+ single_file = True),
+ "outs": attr.output_list(mandatory = True),
+ "multiple_outputs": attr.bool(),
+}
+
+jsonnet_to_json = rule(
+ _jsonnet_to_json_impl,
+ attrs = _jsonnet_to_json_attrs + _jsonnet_common_attrs,
+)