Developers

Code Layout

Each language {lang} has a top-level subdirectory that contains:

  1. docs/{lang}.rst: Generated documentation for the language rules

  1. {lang}/repositories.bzl: Macro functions that declare repository rule dependencies for that language

  2. {lang}/{rule}.bzl: Rule implementations of the form {lang}_{kind}_{type}, where kind is one of proto|grpc and type is one of compile|library

  3. {lang}/BUILD.bazel: proto_plugin() declarations for the available plugins for the language

  4. example/{lang}/{rule}/: Generated WORKSPACE and BUILD.bazel demonstrating standalone usage of the rules

  5. {lang}/example/routeguide/: Example routeguide example implementation, if possible

Rule Generation

To help maintain consistency of the rule implementations and documentation, all of the rule implementations are generated by the tool //tools/rulegen. Changes in the main README.rst should be placed in tools/rulegen/README.header.rst or tools/rulegen/README.footer.rst`. Changes to generated rules should be put in the source files (example: tools/rulegen/java.go).

Developing Custom Plugins

Generally, follow the pattern seen in the multiple language examples in this repository. The basic idea is:

  1. Load the plugin rule: load("@rules_proto_grpc//:defs.bzl", "proto_plugin")

  2. Define the rule, giving it a name, options (not mandatory), tool and outputs. tool is a label that refers to the binary executable for the plugin itself

  3. Choose your output type (pick one!):
    • outputs: A list of strings patterns that predicts the pattern of files generated by the plugin. For plugins that produce one output file per input proto file

    • out: The name of a single output file generated by the plugin

    • output_directory: Set to true if your plugin generates files in a non-predictable way. e.g. if the output paths depend on the service names within the files

  4. Create a compilation rule and aspect using the following template:

load("@rules_proto//proto:defs.bzl", "ProtoInfo")
load(
    "@rules_proto_grpc//:defs.bzl",
    "ProtoLibraryAspectNodeInfo",
    "ProtoPluginInfo",
    "proto_compile_aspect_attrs",
    "proto_compile_aspect_impl",
    "proto_compile_attrs",
    "proto_compile_impl",
)

# Create aspect
example_aspect = aspect(
    implementation = proto_compile_aspect_impl,
    provides = [ProtoLibraryAspectNodeInfo],
    attr_aspects = ["deps"],
    attrs = dict(
        proto_compile_aspect_attrs,
        _plugins = attr.label_list(
            doc = "List of protoc plugins to apply",
            providers = [ProtoPluginInfo],
            default = [
                Label("//<LABEL OF YOUR PLUGIN>"),
            ],
        ),
        _prefix = attr.string(
            doc = "String used to disambiguate aspects when generating outputs",
            default = "example_aspect",
        ),
    ),
    toolchains = ["@rules_proto_grpc//protobuf:toolchain_type"],
)

# Create compile rule to apply aspect
_rule = rule(
    implementation = proto_compile_impl,
    attrs = dict(
        proto_compile_attrs,
        protos = attr.label_list(
            mandatory = False,  # TODO: set to true in 4.0.0 when deps removed below
            providers = [ProtoInfo],
            doc = "List of labels that provide the ProtoInfo provider (such as proto_library from rules_proto)",
        ),
        deps = attr.label_list(
            mandatory = False,
            providers = [ProtoInfo, ProtoLibraryAspectNodeInfo],
            aspects = [example_aspect],
            doc = "DEPRECATED: Use protos attr",
        ),
        _plugins = attr.label_list(
            providers = [ProtoPluginInfo],
            default = [
                Label("//<LABEL OF YOUR PLUGIN>"),
            ],
            doc = "List of protoc plugins to apply",
        ),
    ),
    toolchains = [str(Label("//protobuf:toolchain_type"))],
)

# Create macro for converting attrs and passing to compile
def example_compile(**kwargs):
    _rule(
        verbose_string = "{}".format(kwargs.get("verbose", 0)),
        **kwargs
    )