0

I have a yaml file in a Bazel monorepo that has constants I'd like to use in several languages. This is kind of like how protobuffs are created and used.

How can I parse this yaml file at build time and depend on the outputs?

For instance:

item1: "hello"
item2: "world"
nested:
  nested1: "I'm nested"
  nested2: "I'm also nested"

I then need to parse this yaml file so it can be used in many different languages (e.g., Rust, TypeScript, Python, etc.). For instance, here's the desired output for TypeScript:

export default {
  item1: "hello",
  item2: "world",
  nested: {
    nested1: "I'm nested",
    nested2: "I'm also nested",
  }
}

Notice, I don't want TypeScript code that reads the yaml file and converts it into an object. That conversion should be done in the build process.

For the actual conversion, I'm thinking of writing that in Python, but it doesn't need to be. This would then mean the python also needs to run at build time.


P.S. I care mostly about the functionality, so I'm flexible with the exactly how it's done. I'm even fine using another file format aside from yaml.

Nick
  • 5,108
  • 2
  • 25
  • 58
  • https://docs.bazel.build/versions/main/be/general.html#genrule – oakad Oct 24 '21 at 05:44
  • It seems like genrule only runs terminal commands? Which would mean if I were to use a Python script (or other language) to parse the yaml, it wouldn't be hermetic. Or am I missing something? – Nick Oct 24 '21 at 14:36
  • What do you mean by "hermetic"? Genrule can take any label as "executable" and run it against one of more source labels to produce a target label. If you don't know the name of the target label in advance you can create a custom rule returning `DefaultInfo` (or any other *Info object to do about anything possible). It's only slightly more difficult. – oakad Oct 24 '21 at 16:33

1 Answers1

1

Thanks to help from @oakad, I was able to figure it out. Essentially, you can use genrule to create outputs.

So, assuming you have some target like python setup to generate the output named parse_config, you can just do this:

genrule(
    name = "generated_output",
    srcs = [],
    outs = ["output.txt"],
    cmd = "./$(execpath :parse_config) > $@" % name,
    exec_tools = [":parse_config"],
    visibility = ["//visibility:public"],
)

The generated file is output.txt. And you can access it via //lib/config:generated_output.

Note, essentially the cmd is piping the stdout into the file contents. In Python that means anything printed will appear in the generated file.

Nick
  • 5,108
  • 2
  • 25
  • 58
  • 1
    Couple nitpicks: 1) from the docs: `Users are encouraged to prefer exec_tools to tools where this does not cause any issues` 2) using `$(location ...) is "legacy pre-Starlark behavior and not recommended" pretty sure using `$(rootpath ...) is appropriate https://docs.bazel.build/versions/main/be/general.html#genrule.exec_tools https://docs.bazel.build/versions/main/be/make-variables.html#predefined_label_variables – Sam Oct 28 '21 at 02:03
  • Thanks! Updated those two things and they work on my machine. Updated my answer to make sure others follow the best practice as well. – Nick Oct 28 '21 at 21:58