diff options
author | David Chen <dzc@google.com> | 2015-09-12 01:32:26 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2015-09-14 15:39:18 +0000 |
commit | 361d2e2f7448038355b337ee4f7191a28c20755a (patch) | |
tree | 705fb5e52a6a36300d11cf2c0f451f2b9324ae7b /tools | |
parent | 2ad1c81755063b72a599844e730e0344344f61e8 (diff) |
Add Jsonnet rules to Bazel.
RELNOTES: Add Jsonnet rules to Bazel
--
MOS_MIGRATED_REVID=102895524
Diffstat (limited to 'tools')
-rw-r--r-- | tools/BUILD | 1 | ||||
-rw-r--r-- | tools/build_defs/jsonnet/BUILD | 16 | ||||
-rw-r--r-- | tools/build_defs/jsonnet/README.md | 310 | ||||
-rw-r--r-- | tools/build_defs/jsonnet/jsonnet.BUILD | 80 | ||||
-rw-r--r-- | tools/build_defs/jsonnet/jsonnet.WORKSPACE | 6 | ||||
-rw-r--r-- | tools/build_defs/jsonnet/jsonnet.bzl | 139 |
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, +) |