Skip to content

stackb/protoc-gen-starlark

Repository files navigation

CI

protoc-gen-starlark

protobuf starlark

protoc-gen-starlark is a scriptable protocol buffer plugin. It might just be the the easiest way to write a protoc plugin! 😍

Installation

Download a binary from the releases page, or install from source:

go install github.com/stackb/protoc-gen-starlark/cmd/protoc-gen-starlark@latest

Usage

protoc-gen-starlark works like any other typical protoc plugin: it reads an encoded CodeGeneratorRequest from stdin and writes an encoded CodeGeneratorResponse to stdout.

The logic of generating a response is performed within a starlark script that you must write. The simplest such script looks something like:

pb = proto.package("google.protobuf.compiler")

def generate(request):
    """generate prepares the response.

    Args:
      request: the pb.CodeGeneratorRequest that was read from stdin.
    Returns:
      a pb.CodeGeneratorResponse
    """
    return pb.CodeGeneratorResponse(
        error = "not implemented",
    )

def main(ctx):
    """main is the entrypoint function.

    Args:
      ctx: the script context.  It has a struct member named
      'vars' which is a StringDict of variables injected into
      the entrypoint.  vars is guaranteed to have an entry named
      "request" that is the pb.CodeGeneratorRequest read from stdin.
    Returns:
      A single pb.CodeGeneratorResponse.  Per skycfg semantics,
      the return value from `main` must be a list however so it
      is wrapped accordingly.

    """
    return [generate(ctx.vars["request"])]

Although starlark is an interpreted language, the protobuf message types are stongly typed: it is an error to set/get fields that are not part of the message definition. See stackb/grpc-starlark and stripe/skycfg for more details about this.

protoc-gen-starlark is built using stackb/grpc-starlark and shares the same command line flags.

A sample protoc invocation might look something like:

$ export PROTOC_GEN_STARLARK_SCRIPT=foo.star
$ protoc \
  --foo_out=./gendir \
  --plugin=protoc-gen-foo=/usr/bin/protoc-gen-starlark

In this case protoc-gen-starlark discovers which script to evaluate using the PROTOC_GEN_STARLARK_SCRIPT environment variable.

Another strategy is to copy/rename protoc-gen-starlark and the script to a common name. If a file named $0.star exists where $0 is the name of the executable itself, this will be loaded. For example:

$ ln -s /usr/bin/protoc-gen-starlark tools/protoc-gen-foo
$ mv foo.plugin.star                 tools/protoc-gen-foo.star
$ ln -s /usr/bin/protoc-gen-starlark tools/protoc-gen-bar
$ mv bar.plugin.star                 tools/protoc-gen-bar.star

$ protoc \
  --foo_out=./gendir \
  --plugin=protoc-gen-foo=./tools/protoc-gen-foo
  --bar_out=./gendir \
  --plugin=protoc-gen-bar=./tools/protoc-gen-bar

Alternatively, a shell script can be used to wrap the invocation of the plugin:

#!/bin/bash

# protoc-gen-foo.sh wraps protoc-gen-starlark and sets
# the file argument explicitly.

set -euox pipefail

/usr/bin/protoc-gen-starlark \
    -file ./tools/protoc-gen-foo.star
$ protoc \
  --foo_out=./gendir \
  --plugin=protoc-gen-foo=./tools/protoc-gen-foo.sh

By default, the message types from plugin.proto are pre-loaded. You can make additional types available to proto.package using the --protoset=/path/to/a/descriptor_set_out.pb flag.

About

Scriptable protoc plugins

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published