bazel-contrib / rules_jsonnet

Jsonnet rules for Bazel
https://bazelbuild.github.io/rules_jsonnet/
Apache License 2.0
68 stars 73 forks source link

doc: describe how to import external dependencies #139

Closed oncilla closed 7 months ago

oncilla commented 3 years ago

First of all, thanks for this great rule set. Makes my life a lot easier :+1:

We are using jsonnet to build grafana dashboards using the external dependency https://github.com/grafana/grafonnet-lib.

Currently, I did not find any documentation on how to do it with this rule set. After some playing around, I got a working example. (see below)

Now, is this the canonical way of doing this, or did I miss a simpler way? In particular, imports = ["external/com_github_grafana_grafonnet-lib"], looks a bit odd to me.

If It is the way to go, I would be happy to contribute some documentation on how to import external dependencies.

My solution: (https://github.com/Oncilla/rules_jsonnet_examples/tree/main/grafonnet)

# BUILD.bazel
jsonnet_to_json_test(
    name = "prometheus_test",
    src = "prometheus.jsonnet",
    golden = "prometheus_compiled.json",
    imports = ["external/com_github_grafana_grafonnet-lib"],
    deps = ["@com_github_grafana_grafonnet-lib//:grafonnet"],
)
# WORKSPACE
http_archive(
    name = "com_github_grafana_grafonnet-lib",
    build_file_content = """
load(
    "@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl",
    "jsonnet_library",
)
jsonnet_library(
    name = "grafonnet",
    srcs = glob(["grafonnet/*"]),
    visibility = ["//visibility:public"],
)
""",
    sha256 = "571ff5e6a44b583b8069476fb720f70a97a39734d206eae41c1c4f12af0a05d1",
    strip_prefix = "grafonnet-lib-0.1.0",
    urls = ["https://github.com/grafana/grafonnet-lib/archive/v0.1.0.zip"],
)
oncilla commented 3 years ago

I was a bit too quick to judge. Turns out this worked because of a happy accident. For nested packages, this is not so straight forward.

Consider the following example: https://github.com/Oncilla/rules_jsonnet_examples/tree/main/grafonnet/nested

@rules_jsonnet_examples/grafonnet$ bazel test  //nested:prometheus
INFO: Analyzed target //nested:prometheus (0 packages loaded, 0 targets configured).
INFO: Found 1 target and 0 test targets...
ERROR: /home/roosd/go/src/github.com/oncilla/rules_jsonnet_examples/grafonnet/nested/BUILD.bazel:3:16: Compiling Jsonnet to JSON for prometheus failed: (Exit 1): bash failed: error executing command /bin/bash -c ... (remaining 1 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox bash failed: error executing command /bin/bash -c ... (remaining 1 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox
RUNTIME ERROR: couldn't open import "grafonnet/grafana.libsonnet": no match locally or in the Jsonnet library paths
        nested/prometheus.jsonnet:3:17-53       thunk <grafana> from <$>
        nested/prometheus.jsonnet:4:19-26
        nested/prometheus.jsonnet:61:1-10
        During evaluation

Target //nested:prometheus failed to build

Verbose output shows that the following command is run.

/bin/bash -c 'set -e; bazel-out/host/bin/external/jsonnet_go/cmd/jsonnet/linux_amd64_stripped/jsonnet -J nested/external/com_github_grafana_grafonnet-lib -J . -J bazel-out/k8-fastbuild/bin -J bazel-out/k8-fastbuild/bin nested/prometheus.jsonnet -o bazel-out/k8-fastbuild/bin/nested/prometheus.json'

Thus, the problem is -J nested/external/com_github_grafana_grafonnet-lib which prepends the package path.

I was able to solve that in the //nested:prometheus_hack target, but the hack is pretty ugly, especially for deeply nested packages.

jsonnet_to_json(
    name = "prometheus_hack",
    src = "prometheus.jsonnet",
    imports = ["../external/com_github_grafana_grafonnet-lib"],
    deps = ["@com_github_grafana_grafonnet-lib//:grafonnet"],
    outs = ["prometheus_hack.json"],
)

So, looking forward to hearing what the proper way is :sweat_smile:

sonic-fast-boi commented 3 years ago

After a long fight I was able to get it working without the hacky ../ imports! rules_jsonnet handles transitive deps for you, so all you need to do is define the imports in the jsonnet_library definition in the grafonnet build file.

In my case I wanted to import grafonnet files as "grafonnet/grafana.libsonnet" from my jsonnet source, so I just added . to the imports for the grafonnet jsonnet_library target (I think just an empty string should work as well). Here is the build_file I'm using in the http_archive which is pulling in grafonnet:

load("@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl", "jsonnet_library")

jsonnet_library(
  name = "grafonnet",
  srcs = glob(["grafonnet/*.libsonnet"]),
  visibility = ["//visibility:public"],
  imports = ["."],
)

This will add the appropriate -J flag (-J external/com_github_grafana_grafonnet-lib/.) for grafonnet whenever you use it as a deps in another jsonnet_library, jsonnet_to_json, etc. I was able to see these flags and debug by using the --sandbox_debug flag. With this I'm able to put a target like the following anywhere in my project:

load("@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl", "jsonnet_to_json")

jsonnet_to_json(
    name = "prometheus",
    src = "prometheus.jsonnet",
    outs = ["prometheus.json"],
    deps = [
      "@com_github_grafana_grafonnet-lib//:grafonnet",
    ],
)
oncilla commented 3 years ago

@sonic-fast-boi this works. Thank you so much for this :+1:

jwgmatthews commented 2 years ago

I think this can be fixed with the following patch to this repo:

--- jsonnet/jsonnet.bzl
+++ jsonnet/jsonnet.bzl
@@ -50,7 +50,9 @@
 def _add_prefix_to_imports(label, imports):
     imports_prefix = ""
+    workspace_import = []
     if label.workspace_root:
         imports_prefix += label.workspace_root + "/"
+        workspace_import = [label.workspace_root]
     if label.package:
         imports_prefix += label.package + "/"
-    return [imports_prefix + im for im in imports]
+    return workspace_import + [imports_prefix + im for im in imports]

This "does the right thing" without needing to add imports = ["."] and also works if the desired library is not at the top level of the repository (imports = ["."] would then add -J external/some-repo/path/to/the/rule rather than -J external/some-repo). This solves the problem when using e.g. with https://github.com/yugui/jsonnetunit.

EdSchouten commented 7 months ago

This has been fixed in #182. It no longer matters whether something is an external dependency or not. It can always be imported using a path relative to its workspace root, or relative to one of the provided imports. Thanks for reporting this issue!