diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index b273d67..91f0bb0 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -15,18 +15,32 @@ tasks: name: docs platform: ${{ platform }} working_directory: docs + test_targets: + - //... + + examples_go: + name: examples + platform: ${{ platform }} + working_directory: examples + test_flags: + - "--extra_toolchains=@rules_jsonnet//jsonnet:go_jsonnet_toolchain" + test_targets: + - //... + + examples_cpp: + name: examples + platform: ${{ platform }} + working_directory: examples test_flags: - - --define - - jsonnet_port=${{ jsonnet_port }} + - "--extra_toolchains=@rules_jsonnet//jsonnet:cpp_jsonnet_toolchain" test_targets: - //... - examples: + examples_rust: name: examples platform: ${{ platform }} working_directory: examples test_flags: - - --define - - jsonnet_port=${{ jsonnet_port }} + - "--extra_toolchains=@rules_jsonnet//jsonnet:rust_jsonnet_toolchain" test_targets: - //... diff --git a/.github/workflows/create_archive_and_notes.sh b/.github/workflows/create_archive_and_notes.sh index aafc1ee..f78af7b 100755 --- a/.github/workflows/create_archive_and_notes.sh +++ b/.github/workflows/create_archive_and_notes.sh @@ -58,5 +58,7 @@ jsonnet_go_repositories() load("@google_jsonnet_go//bazel:deps.bzl", "jsonnet_go_dependencies") jsonnet_go_dependencies() + +register_toolchains("@io_bazel_rules_jsonnet//jsonnet:go_jsonnet_toolchain") \`\`\` EOF diff --git a/MODULE.bazel b/MODULE.bazel index 8cf1d2b..44abe8d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,3 +7,50 @@ module( bazel_dep(name = "bazel_skylib", version = "1.5.0") bazel_dep(name = "jsonnet", version = "0.20.0") bazel_dep(name = "jsonnet_go", version = "0.20.0", repo_name = "google_jsonnet_go") + +bazel_dep(name = "rules_rust", version = "0.45.1") + +jsonnet = use_extension("//jsonnet:extensions.bzl", "jsonnet") +use_repo(jsonnet, "rules_jsonnet_toolchain") +register_toolchains("@rules_jsonnet_toolchain//:toolchain") + +rust_host = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools") +rust_host.host_tools( + version = "nightly/2024-05-02", +) + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain( + edition = "2021", + # Nightly version is required to be able to depend on a binary dependency + # with Cargo. + # See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies. + versions = ["nightly/2024-05-01"], + sha256s = { + "2024-05-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "3c3d4693b8e846c9c250bb14a97654657ca62a5eb20617a875c34462582daf83", + "2024-05-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "379e22e343a7b1c2e39b030810ff633d800d0daae0f8fd6bf00181402c42be4e", + "2024-05-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "899aec41e675146359d30a296db488f114cd280aef9dea566e178a8bb6f33774", + "2024-05-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "4b04ceaf724bea888fb1e5e6bd9a9a963196f585d4f73036e783a2c51d4e907e", + "2024-05-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "bf52ea3e1ac669455694079fb83b6bd0d446e759b9ab3502f75615d538e646a0", + }, +) + +use_repo(rust, "rust_toolchains") +register_toolchains("@rust_toolchains//:all") + +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") +crate.spec( + package = "jrsonnet", + version = "0.5.0-pre95", + # Binary artifacts can't be depended upon without specifically marking the + # artifact as `bin`. + artifact = "bin", +) + +# Required for rules_rust to generate binary targets for the Jrsonnet crate. +crate.annotation( + crate = "jrsonnet", + gen_binaries = ["jrsonnet"], +) +crate.from_specs() +use_repo(crate, "crates") diff --git a/README.md b/README.md index 1e492cf..b89e9aa 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,56 @@ These are build rules for working with [Jsonnet][jsonnet] files with Bazel. To use the Jsonnet rules as part of your Bazel project, please follow the instructions on [the releases page](https://github.com/bazelbuild/rules_jsonnet/releases). -## Jsonnet Port Selection +## Jsonnet Compiler Selection -By default, Bazel will use [the Go port](https://github.com/google/go-jsonnet) of Jsonnet. To use [the C++ port](https://github.com/google/jsonnet) of Jsonnet instead, invoke Bazel with the `--define jsonnet_port=cpp` command-line flag. To select the Go port explicitly, invoke Bazel with the `--define jsonnet_port=go` command-line flag. +By default for Bzlmod, Bazel will use the [Go +compiler](https://github.com/google/go-jsonnet). Note that the +primary development focus of the Jsonnet project is now with the Go compiler. +This repository's support for using the C++ compiler is deprecated, and may be +removed in a future release. -_bazel_ Flag | Jsonnet Port ------------- | ------------ -(none) | Go -`--define jsonnet_port=cpp`| C++ -`--define jsonnet_port=go` | Go +To use [the +C++](https://github.com/google/jsonnet) or +[Rust](https://github.com/CertainLach/jrsonnet) compiler of Jsonnet instead, +register a different compiler: -Note that the primary development focus of the Jsonnet project is now with the Go port. This repository's support for using the C++ port is deprecated, and may be removed in a future release. +| Jsonnet compiler | MODULE.bazel directive | WORKSPACE directive | +| ---------------- | --------------------------------- | -------------------------------------------------------------------------------- | +| Go | `jsonnet.compiler(name = "go")` | `register_toolchains("@io_bazel_rules_jsonnet//jsonnet:go_jsonnet_toolchain")` | +| cpp | `jsonnet.compiler(name = "cpp")` | `register_toolchains("@io_bazel_rules_jsonnet//jsonnet:cpp_jsonnet_toolchain")` | +| Rust | `jsonnet.compiler(name = "rust")` | `register_toolchains("@io_bazel_rules_jsonnet//jsonnet:rust_jsonnet_toolchain")` | + +Note that `WORKSPACE` users must register a toolchain manually, using the table +above as reference. + +### Rust Jsonnet Compiler + +To use the Rust Jsonnet compiler a `Nightly` Rust version for the host tools is +required because `-Z bindeps` is needed to compile the Jrsonnet binary. + +Add the following snippet to the `Module.bazel` file: + +```Starlark +bazel_dep(name = "rules_rust", version = "0.45.1") + +rust_host = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools") +rust_host.host_tools( + version = "nightly/2024-05-02", +) +``` + +### CLI + +Use the `--extra_toolchains` flag to pass the preferred toolchain to the bazel +invocation: + +```bash +bazel build //... --extra_toolchains=@rules_jsonnet//jsonnet:cpp_jsonnet_toolchain + +bazel test //... --extra_toolchains=@rules_jsonnet//jsonnet:rust_jsonnet_toolchain + +bazel run //... --extra_toolchains=@rules_jsonnet//jsonnet:go_jsonnet_toolchain +``` ## jsonnet_library diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index 713c582..f565c9e 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -2,7 +2,7 @@ load("@aspect_bazel_lib//lib:docs.bzl", "stardoc_with_diff_test", "update_docs") stardoc_with_diff_test( name = "jsonnet", - bzl_library_target = "@rules_jsonnet//jsonnet:jsonnet", + bzl_library_target = "@rules_jsonnet//jsonnet:docs", ) update_docs( diff --git a/docs/jsonnet.md b/docs/jsonnet.md index 88df332..ab9ea97 100644 --- a/docs/jsonnet.md +++ b/docs/jsonnet.md @@ -1,6 +1,6 @@ -Jsonnet Rules +# Jsonnet Rules These are build rules for working with [Jsonnet][jsonnet] files with Bazel. @@ -16,7 +16,7 @@ instructions on [the releases page](https://github.com/bazelbuild/rules_jsonnet/ ## jsonnet_library
-jsonnet_library(name, deps, srcs, data, imports, jsonnet) +jsonnet_library(name, deps, srcs, data, imports)Creates a logical set of Jsonnet files. @@ -60,7 +60,6 @@ Example: | srcs | List of `.jsonnet` files that comprises this Jsonnet library | List of labels | optional | `[]` | | data | - | List of labels | optional | `[]` | | imports | List of import `-J` flags to be passed to the `jsonnet` compiler. | List of strings | optional | `[]` | -| jsonnet | A jsonnet binary | Label | optional | `"@rules_jsonnet//jsonnet:jsonnet_tool"` | @@ -70,7 +69,7 @@ Example:
jsonnet_to_json(name, deps, src, data, outs, code_vars, ext_code, ext_code_envs, ext_code_file_vars, ext_code_files, ext_str_envs, ext_str_file_vars, ext_str_files, ext_strs, extra_args, - imports, jsonnet, multiple_outputs, out_dir, stamp_keys, tla_code, tla_code_envs, + imports, multiple_outputs, out_dir, stamp_keys, tla_code, tla_code_envs, tla_code_files, tla_str_envs, tla_str_files, tla_strs, vars, yaml_stream)@@ -200,7 +199,6 @@ Example: | ext_strs | - | Dictionary: String -> String | optional | `{}` | | extra_args | Additional command line arguments for the Jsonnet interpreter.
local foo = import "foo.jsonnet";
{
"foo.json": foo,
}
jsonnet_to_json_test(name, deps, src, data, canonicalize_golden, code_vars, error, ext_code, ext_code_envs, ext_code_file_vars, ext_code_files, ext_str_envs, - ext_str_file_vars, ext_str_files, ext_strs, extra_args, golden, imports, jsonnet, + ext_str_file_vars, ext_str_files, ext_strs, extra_args, golden, imports, output_file_contents, regex, stamp_keys, tla_code, tla_code_envs, tla_code_files, tla_str_envs, tla_str_files, tla_strs, vars, yaml_stream)@@ -348,7 +346,6 @@ Example: | extra_args | Additional command line arguments for the Jsonnet interpreter.
-JsonnetLibraryInfo() +jsonnet_toolchain(name, compiler, create_directory_flags, manifest_file_support)+The Jsonnet compiler information. +**ATTRIBUTES** -**FIELDS** - - - - - -## jsonnet_repositories - -
-jsonnet_repositories() -- -Adds the external dependencies needed for the Jsonnet rules. +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| compiler | - | Label | optional | `None` | +| create_directory_flags | The flags passed to the Jsonnet compiler when a directory must be created. | List of strings | required | | +| manifest_file_support | If the Jsonnet compiler supports writing the output filenames to a manifest file. | Boolean | required | | diff --git a/examples/BUILD b/examples/BUILD index 105c314..c9c2eab 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -1,11 +1,11 @@ +load("@rules_jsonnet//jsonnet:jsonnet.bzl", "jsonnet_library", "jsonnet_to_json", "jsonnet_to_json_test") + package(default_visibility = ["//visibility:public"]) # This directory contains unit and regression tests that also serve as examples # for jsonnet rules. The BUILD rules should not contain any jsonnet_to_json # rules as this is redundant with jsonnet_to_json_test rules. -load("@rules_jsonnet//jsonnet:jsonnet.bzl", "jsonnet_library", "jsonnet_to_json", "jsonnet_to_json_test") - jsonnet_library( name = "workflow", srcs = ["workflow.libsonnet"], diff --git a/examples/MODULE.bazel b/examples/MODULE.bazel index 7d190ee..5057cd2 100644 --- a/examples/MODULE.bazel +++ b/examples/MODULE.bazel @@ -9,8 +9,24 @@ local_path_override( path = "..", ) +jsonnet = use_extension("@rules_jsonnet//jsonnet:extensions.bzl", "jsonnet") +jsonnet.compiler(name = "go") + bazel_dep(name = "other_module", version = "0.0.0") local_path_override( module_name = "other_module", path = "other_module", ) + +bazel_dep(name = "other_toolchain_module", version = "0.0.0") +local_path_override( + module_name = "other_toolchain_module", + path = "other_toolchain_module", +) + +bazel_dep(name = "rules_rust", version = "0.45.1") + +rust_host = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools") +rust_host.host_tools( + version = "nightly/2024-05-02", +) diff --git a/examples/invalid.out b/examples/invalid.out index d8e6fb8..7cf13bd 100644 --- a/examples/invalid.out +++ b/examples/invalid.out @@ -1,3 +1 @@ -^RUNTIME ERROR: Foo\. - .*invalid\.jsonnet:15:1-13 ($|\$ - During evaluation ) +^RUNTIME ERROR: Foo.*invalid\.jsonnet:15:1-13* diff --git a/examples/other_module/MODULE.bazel b/examples/other_module/MODULE.bazel index 1287d51..b4d6cdb 100644 --- a/examples/other_module/MODULE.bazel +++ b/examples/other_module/MODULE.bazel @@ -4,3 +4,6 @@ module( ) bazel_dep(name = "rules_jsonnet", version = "0.0.0") + +jsonnet = use_extension("@rules_jsonnet//jsonnet:extensions.bzl", "jsonnet") +jsonnet.compiler(name = "rust") diff --git a/examples/other_toolchain_module/MODULE.bazel b/examples/other_toolchain_module/MODULE.bazel new file mode 100644 index 0000000..6c1898b --- /dev/null +++ b/examples/other_toolchain_module/MODULE.bazel @@ -0,0 +1,13 @@ +# This module 'other_toolchain_module' is here to test multiple modules +# providing different toolchains and the logic in the toolchain conflict +# resolution logic. + +module( + name = "other_toolchain_module", + version = "0.0.0", +) + +bazel_dep(name = "rules_jsonnet", version = "0.0.0") + +jsonnet = use_extension("@rules_jsonnet//jsonnet:extensions.bzl", "jsonnet") +jsonnet.compiler(name = "rust") diff --git a/examples/out_dir.jsonnet b/examples/out_dir.jsonnet index 8551ed0..5421829 100644 --- a/examples/out_dir.jsonnet +++ b/examples/out_dir.jsonnet @@ -1,5 +1,4 @@ { 'hello.txt': 'Hello, Bazel!', 'goodbye.txt': 'Goodbye, Bazel!', - 'nested/nested.txt': 'This file is in a nested directory.', } diff --git a/jsonnet/BUILD b/jsonnet/BUILD index ead6a25..f45f57d 100644 --- a/jsonnet/BUILD +++ b/jsonnet/BUILD @@ -1,6 +1,7 @@ +load(":toolchain.bzl", "jsonnet_toolchain") load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -exports_files(["jsonnet.bzl"]) +exports_files(["docs.bzl", "jsonnet.bzl", "toolchain.bzl"]) bzl_library( name = "bzl_srcs", @@ -8,8 +9,12 @@ bzl_library( ) bzl_library( - name = "jsonnet", - srcs = ["jsonnet.bzl"], + name = "docs", + srcs = [ + "docs.bzl", + "jsonnet.bzl", + "toolchain.bzl", + ], visibility = ["//visibility:public"], deps = [ ":bzl_srcs", @@ -25,25 +30,46 @@ py_binary( visibility = ["//visibility:public"], ) -config_setting( - name = "port_cpp", - define_values = { - "jsonnet_port": "cpp", - }, +toolchain_type(name = "toolchain_type") + +jsonnet_toolchain( + name = "rust_jsonnet", + compiler = "@crates//:jrsonnet__jrsonnet", + create_directory_flags = ["-c"], + manifest_file_support = False, +) + +jsonnet_toolchain( + name = "go_jsonnet", + compiler = "@google_jsonnet_go//cmd/jsonnet", + create_directory_flags = ["-c"], + manifest_file_support = True, +) + +jsonnet_toolchain( + name = "cpp_jsonnet", + compiler = "@jsonnet//cmd:jsonnet", + create_directory_flags = [], + manifest_file_support = True, ) -config_setting( - name = "port_go", - define_values = { - "jsonnet_port": "go", - }, +toolchain( + name = "rust_jsonnet_toolchain", + toolchain = ":rust_jsonnet", + toolchain_type = ":toolchain_type", + visibility = ["//visibility:public"], +) + +toolchain( + name = "go_jsonnet_toolchain", + toolchain = ":go_jsonnet", + toolchain_type = ":toolchain_type", + visibility = ["//visibility:public"], ) -alias( - name = "jsonnet_tool", - actual = select({ - "//jsonnet:port_cpp": "@jsonnet//cmd:jsonnet", - "//conditions:default": "@google_jsonnet_go//cmd/jsonnet", - }), +toolchain( + name = "cpp_jsonnet_toolchain", + toolchain = ":cpp_jsonnet", + toolchain_type = ":toolchain_type", visibility = ["//visibility:public"], ) diff --git a/jsonnet/docs.bzl b/jsonnet/docs.bzl new file mode 100644 index 0000000..9b2aa9b --- /dev/null +++ b/jsonnet/docs.bzl @@ -0,0 +1,25 @@ +"""\ +# Jsonnet Rules + +These are build rules for working with [Jsonnet][jsonnet] files with Bazel. + +[jsonnet]: https://jsonnet.org/ + +## Setup + +To use the Jsonnet rules as part of your Bazel project, please follow the +instructions on [the releases page](https://github.com/bazelbuild/rules_jsonnet/releases). +""" + +load("//jsonnet:toolchain.bzl", _jsonnet_toolchain = "jsonnet_toolchain") +load( + "//jsonnet:jsonnet.bzl", + _jsonnet_library = "jsonnet_library", + _jsonnet_to_json = "jsonnet_to_json", + _jsonnet_to_json_test = "jsonnet_to_json_test", +) + +jsonnet_toolchain = _jsonnet_toolchain +jsonnet_library = _jsonnet_library +jsonnet_to_json = _jsonnet_to_json +jsonnet_to_json_test = _jsonnet_to_json_test diff --git a/jsonnet/extensions.bzl b/jsonnet/extensions.bzl new file mode 100644 index 0000000..2cb7a1f --- /dev/null +++ b/jsonnet/extensions.bzl @@ -0,0 +1,68 @@ +def _get_jsonnet_compiler(module_ctx): + """_get_jsonnet_compiler resolves a Jsonnet compiler from the module graph.""" + + modules_with_compiler = [ + module + for module in module_ctx.modules + if module.tags.compiler + ] + + if not modules_with_compiler: + return "go" + + for module in modules_with_compiler: + if len(module.tags.compiler) != 1: + fail( + "Only one compiler can be specified, got: %s" % + [compiler.name for compiler in module.tags.compiler], + ) + + if module.is_root: + return module.tags.compiler[0].name + + compiler_name = modules_with_compiler[0].tags.compiler[0].name + for module in modules_with_compiler: + if module.tags.compiler[0].name != compiler_name: + fail( + "Different compilers specified by different modules, got: %s. " % + [compiler_name, module.tags.compiler[0].name] + + "Specify a compiler in the root module to resolve this.", + ) + + return compiler_name + +def _jsonnet_impl(module_ctx): + _jsonnet_toolchain_repo( + name = "rules_jsonnet_toolchain", + compiler = _get_jsonnet_compiler(module_ctx), + ) + +jsonnet = module_extension( + implementation = _jsonnet_impl, + tag_classes = { + "compiler": tag_class( + attrs = { + "name": attr.string(), + }, + ), + }, +) + +def _jsonnet_toolchain_repo_impl(ctx): + ctx.file( + "BUILD.bazel", + content = """ +alias( + name = "toolchain", + actual = "@io_bazel_rules_jsonnet//jsonnet:%s_jsonnet_toolchain", +) +""" % ctx.attr.compiler, + executable = False, + ) + +_jsonnet_toolchain_repo = repository_rule( + implementation = _jsonnet_toolchain_repo_impl, + attrs = { + "compiler": attr.string(), + }, +) diff --git a/jsonnet/jsonnet.bzl b/jsonnet/jsonnet.bzl index 75a2374..00bb4ec 100644 --- a/jsonnet/jsonnet.bzl +++ b/jsonnet/jsonnet.bzl @@ -11,19 +11,6 @@ # 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 - -These are build rules for working with [Jsonnet][jsonnet] files with Bazel. - -[jsonnet]: https://jsonnet.org/ - -## Setup - -To use the Jsonnet rules as part of your Bazel project, please follow the -instructions on [the releases page](https://github.com/bazelbuild/rules_jsonnet/releases). -""" - load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:shell.bzl", "shell") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") @@ -98,11 +85,6 @@ def _jsonnet_library_impl(ctx): ), ] -def _jsonnet_toolchain(ctx): - return struct( - jsonnet_path = ctx.executable.jsonnet.path, - ) - def _quote(s): return '"' + s.replace('"', '\\"') + '"' @@ -146,7 +128,7 @@ def _make_stamp_resolve(ext_vars, ctx, relative = True): val = "$(cat %s)" % stamp_file.short_path else: val = "$(cat %s)" % stamp_file.path - stamp_inputs += [stamp_file] + stamp_inputs.append(stamp_file) results[key] = val @@ -161,7 +143,6 @@ def _jsonnet_to_json_impl(ctx): print("'code_vars' attribute is deprecated, please use 'ext_code'.") depinfo = _setup_deps(ctx.attr.deps) - toolchain = _jsonnet_toolchain(ctx) jsonnet_ext_strs = ctx.attr.ext_strs or ctx.attr.vars jsonnet_ext_str_envs = ctx.attr.ext_str_envs jsonnet_ext_code = ctx.attr.ext_code or ctx.attr.code_vars @@ -191,7 +172,7 @@ def _jsonnet_to_json_impl(ctx): command = ( [ "set -e;", - toolchain.jsonnet_path, + ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.compiler.path, ] + ["-J " + shell.quote(im) for im in _get_import_paths(ctx.label, [ctx.file.src], ctx.attr.imports)] + ["-J " + shell.quote(im) for im in depinfo.imports.to_list()] + @@ -233,10 +214,15 @@ def _jsonnet_to_json_impl(ctx): # will write the resulting JSON to stdout, which is redirected into # a single JSON output file. if ctx.attr.out_dir: - output_manifest = ctx.actions.declare_file("_%s_outs.mf" % ctx.label.name) + if ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.manifest_file_support: + output_manifest = ctx.actions.declare_file("_%s_outs.mf" % ctx.label.name) + outputs.append(output_manifest) + command += ["-o", output_manifest.path] + out_dir = ctx.actions.declare_directory(ctx.attr.out_dir) - outputs += [out_dir, output_manifest] - command += [ctx.file.src.path, "-c", "-m", out_dir.path, "-o", output_manifest.path] + outputs.append(out_dir) + command += [ctx.file.src.path, "-m", out_dir.path] + command += ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.create_directory_flags elif len(ctx.attr.outs) > 1 or ctx.attr.multiple_outputs: # Assume that the output directory is the leading part of the # directory name that is shared by all output files. @@ -248,16 +234,20 @@ def _jsonnet_to_json_impl(ctx): if part1 != part2: base_dirname = base_dirname[:i] break - - output_manifest = ctx.actions.declare_file("_%s_outs.mf" % ctx.label.name) - outputs += ctx.outputs.outs + [output_manifest] - command += ["-m", "/".join(base_dirname), ctx.file.src.path, "-o", output_manifest.path] + if ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.manifest_file_support: + output_manifest = ctx.actions.declare_file("_%s_outs.mf" % ctx.label.name) + outputs.append(output_manifest) + command += ["-o", output_manifest.path] + + outputs += ctx.outputs.outs + command += ["-m", "/".join(base_dirname), ctx.file.src.path] + command += ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.create_directory_flags elif len(ctx.attr.outs) > 1: fail("Only one file can be specified in outs if multiple_outputs is " + "not set.") else: compiled_json = ctx.outputs.outs[0] - outputs += [compiled_json] + outputs.append(compiled_json) command += [ctx.file.src.path, "-o", compiled_json.path] transitive_data = depset(transitive = [dep.data_runfiles.files for dep in ctx.attr.deps] + @@ -282,11 +272,10 @@ def _jsonnet_to_json_impl(ctx): depinfo.transitive_sources.to_list() ) - tools = [ctx.executable.jsonnet] - ctx.actions.run_shell( inputs = compile_inputs + stamp_inputs, - tools = tools, + toolchain = Label("//jsonnet:toolchain_type"), + tools = [ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.compiler], outputs = outputs, mnemonic = "Jsonnet", command = " ".join(command), @@ -300,6 +289,11 @@ def _jsonnet_to_json_impl(ctx): runfiles = ctx.runfiles(files = [out_dir]), )] + return [DefaultInfo( + files = depset([]), + runfiles = ctx.runfiles(files = []), + )] + _EXIT_CODE_COMPARE_COMMAND = """ EXIT_CODE=$? EXPECTED_EXIT_CODE=%d @@ -329,6 +323,10 @@ fi """ _REGEX_DIFF_COMMAND = """ +# Needed due to rust-jsonnet, go-jsonnet and cpp-jsonnet producing different +# output (casing, text etc). +shopt -s nocasematch + GOLDEN_REGEX=$(%s %s) if [[ ! "$OUTPUT" =~ $GOLDEN_REGEX ]]; then echo "FAIL (regex mismatch): %s" @@ -342,12 +340,11 @@ fi def _jsonnet_to_json_test_impl(ctx): """Implementation of the jsonnet_to_json_test rule.""" depinfo = _setup_deps(ctx.attr.deps) - toolchain = _jsonnet_toolchain(ctx) golden_files = [] diff_command = "" if ctx.file.golden: - golden_files += [ctx.file.golden] + golden_files.append(ctx.file.golden) # Note that we only run jsonnet to canonicalize the golden output if the # expected return code is 0, and canonicalize_golden was not explicitly disabled. @@ -356,7 +353,7 @@ def _jsonnet_to_json_test_impl(ctx): # For legacy reasons, we also disable canonicalize_golden for yaml_streams. canonicalize = not (ctx.attr.yaml_stream or not ctx.attr.canonicalize_golden) - dump_golden_cmd = (ctx.executable.jsonnet.short_path if ctx.attr.error == 0 and canonicalize else "/bin/cat") + dump_golden_cmd = (ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.compiler.short_path if ctx.attr.error == 0 and canonicalize else "/bin/cat") if ctx.attr.regex: diff_command = _REGEX_DIFF_COMMAND % ( dump_golden_cmd, @@ -391,7 +388,7 @@ def _jsonnet_to_json_test_impl(ctx): other_args = ctx.attr.extra_args + (["-y"] if ctx.attr.yaml_stream else []) jsonnet_command = " ".join( - ["OUTPUT=$(%s" % ctx.executable.jsonnet.short_path] + + ["OUTPUT=$(%s" % ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.compiler.short_path] + ["-J " + shell.quote(im) for im in _get_import_paths(ctx.label, [ctx.file.src], ctx.attr.imports)] + ["-J " + shell.quote(im) for im in depinfo.imports.to_list()] + other_args + @@ -435,7 +432,7 @@ def _jsonnet_to_json_test_impl(ctx): ), ] if diff_command: - command += [diff_command] + command.append(diff_command) ctx.actions.write( output = ctx.outputs.executable, @@ -450,7 +447,11 @@ def _jsonnet_to_json_test_impl(ctx): ) test_inputs = ( - [ctx.file.src, ctx.executable.jsonnet] + golden_files + + [ + ctx.file.src, + ctx.toolchains["//jsonnet:toolchain_type"].jsonnetinfo.compiler, + ] + + golden_files + transitive_data.to_list() + depinfo.transitive_sources.to_list() + jsonnet_ext_str_files + @@ -473,13 +474,6 @@ _jsonnet_common_attrs = { "imports": attr.string_list( doc = "List of import `-J` flags to be passed to the `jsonnet` compiler.", ), - "jsonnet": attr.label( - doc = "A jsonnet binary", - default = Label("//jsonnet:jsonnet_tool"), - cfg = "exec", - executable = True, - allow_single_file = True, - ), "deps": attr.label_list( doc = "List of targets that are required by the `srcs` Jsonnet files.", providers = [JsonnetLibraryInfo], @@ -640,6 +634,7 @@ jsonnet_to_json = rule( attrs = dict(_jsonnet_compile_attrs.items() + _jsonnet_to_json_attrs.items() + _jsonnet_common_attrs.items()), + toolchains = ["//jsonnet:toolchain_type"], doc = """\ Compiles Jsonnet code to JSON. @@ -774,6 +769,7 @@ jsonnet_to_json_test = rule( attrs = dict(_jsonnet_compile_attrs.items() + _jsonnet_to_json_test_attrs.items() + _jsonnet_common_attrs.items()), + toolchains = ["//jsonnet:toolchain_type"], executable = True, test = True, doc = """\ diff --git a/jsonnet/toolchain.bzl b/jsonnet/toolchain.bzl new file mode 100644 index 0000000..d2bd3cd --- /dev/null +++ b/jsonnet/toolchain.bzl @@ -0,0 +1,46 @@ +JsonnetToolchainInfo = provider( + doc = "Jsonnet toolchain provider", + fields = { + "compiler": "The file to the Jsonnet compiler", + "create_directory_flags": "The flags to pass when creating a directory.", + "manifest_file_support": ( + "If the Jsonnet compiler supports writing the output filenames to a " + + "manifest file." + ), + }, +) + +def _jsonnet_toolchain_impl(ctx): + toolchain_info = platform_common.ToolchainInfo( + jsonnetinfo = JsonnetToolchainInfo( + compiler = ctx.executable.compiler, + create_directory_flags = ctx.attr.create_directory_flags, + manifest_file_support = ctx.attr.manifest_file_support, + ), + ) + return [toolchain_info] + +jsonnet_toolchain = rule( + implementation = _jsonnet_toolchain_impl, + doc = "The Jsonnet compiler information.", + attrs = { + "compiler": attr.label( + executable = True, + cfg = "exec", + ), + "create_directory_flags": attr.string_list( + mandatory = True, + doc = ( + "The flags passed to the Jsonnet compiler when a directory " + + "must be created." + ), + ), + "manifest_file_support": attr.bool( + mandatory = True, + doc = ( + "If the Jsonnet compiler supports writing the output filenames " + + "to a manifest file." + ), + ), + }, +)