From b3c9f4e0a995e4c2f5e752e698f4d23276538382 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Fri, 12 Jul 2024 06:20:44 -0700 Subject: [PATCH] Implement proto_lang_toolchain rule Enable the Starlark rule only on Bazel versions, that have the Starlark version of ProtoInfo provider. That's needed, because only that exposed _transitive_proto_info field that's used in the rule. PiperOrigin-RevId: 651753437 --- bazel/private/proto_lang_toolchain_rule.bzl | 154 ++++++++++++++++++++ bazel/toolchains/proto_lang_toolchain.bzl | 10 +- 2 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 bazel/private/proto_lang_toolchain_rule.bzl diff --git a/bazel/private/proto_lang_toolchain_rule.bzl b/bazel/private/proto_lang_toolchain_rule.bzl new file mode 100644 index 0000000000..665a4b8561 --- /dev/null +++ b/bazel/private/proto_lang_toolchain_rule.bzl @@ -0,0 +1,154 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Implementation of the proto_lang_toolchain rule.""" + +load("@proto_bazel_features//:features.bzl", "bazel_features") +load("//bazel/common:proto_common.bzl", "proto_common") +load("//bazel/common:proto_info.bzl", "ProtoInfo") +load("//bazel/common:proto_lang_toolchain_info.bzl", "ProtoLangToolchainInfo") +load("//bazel/private:toolchain_helpers.bzl", "toolchains") + +def _rule_impl(ctx): + provided_proto_sources = depset(transitive = [bp[ProtoInfo]._transitive_proto_sources for bp in ctx.attr.blacklisted_protos]).to_list() + + flag = ctx.attr.command_line + if flag.find("$(PLUGIN_OUT)") > -1: + fail("in attribute 'command_line': Placeholder '$(PLUGIN_OUT)' is not supported.") + flag = flag.replace("$(OUT)", "%s") + + plugin = None + if ctx.attr.plugin != None: + plugin = ctx.attr.plugin[DefaultInfo].files_to_run + + if proto_common.INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION: + proto_compiler = ctx.toolchains[toolchains.PROTO_TOOLCHAIN].proto.proto_compiler + protoc_opts = ctx.toolchains[toolchains.PROTO_TOOLCHAIN].proto.protoc_opts + else: + proto_compiler = ctx.attr._proto_compiler.files_to_run + protoc_opts = ctx.fragments.proto.experimental_protoc_opts + + if ctx.attr.protoc_minimal_do_not_use: + proto_compiler = ctx.attr.protoc_minimal_do_not_use.files_to_run + + proto_lang_toolchain_info = ProtoLangToolchainInfo( + out_replacement_format_flag = flag, + output_files = ctx.attr.output_files, + plugin_format_flag = ctx.attr.plugin_format_flag, + plugin = plugin, + runtime = ctx.attr.runtime, + provided_proto_sources = provided_proto_sources, + proto_compiler = proto_compiler, + protoc_opts = protoc_opts, + progress_message = ctx.attr.progress_message, + mnemonic = ctx.attr.mnemonic, + allowlist_different_package = ctx.attr.allowlist_different_package, + toolchain_type = ctx.attr.toolchain_type.label if ctx.attr.toolchain_type else None, + ) + return [ + DefaultInfo(files = depset(), runfiles = ctx.runfiles()), + platform_common.ToolchainInfo(proto = proto_lang_toolchain_info), + # TODO: remove when --incompatible_enable_proto_toolchains is flipped and removed + proto_lang_toolchain_info, + ] + +proto_lang_toolchain = rule( + _rule_impl, + doc = """ +

If using Bazel, please load the rule from +https://github.com/bazelbuild/rules_proto. + +

Specifies how a LANG_proto_library rule (e.g., java_proto_library) should invoke the +proto-compiler. +Some LANG_proto_library rules allow specifying which toolchain to use using command-line flags; +consult their documentation. + +

Normally you should not write those kind of rules unless you want to +tune your Java compiler. + +

There's no compiler. The proto-compiler is taken from the proto_library rule we attach to. It is +passed as a command-line flag to Blaze. +Several features require a proto-compiler to be invoked on the proto_library rule itself. +It's beneficial to enforce the compiler that LANG_proto_library uses is the same as the one +proto_library does. + +

Examples

+ +

A simple example would be: +


+proto_lang_toolchain(
+    name = "javalite_toolchain",
+    command_line = "--javalite_out=shared,immutable:$(OUT)",
+    plugin = ":javalite_plugin",
+    runtime = ":protobuf_lite",
+)
+
+ """, + attrs = { + "progress_message": attr.string(default = "Generating proto_library %{label}", doc = """ +This value will be set as the progress message on protoc action."""), + "mnemonic": attr.string(default = "GenProto", doc = """ +This value will be set as the mnemonic on protoc action."""), + "command_line": attr.string(mandatory = True, doc = """ +This value will be passed to proto-compiler to generate the code. Only include the parts +specific to this code-generator/plugin (e.g., do not include -I parameters) +"""), + "output_files": attr.string(values = ["single", "multiple", "legacy"], default = "legacy", doc = """ +Controls how $(OUT) in command_line is formatted, either by +a path to a single file or output directory in case of multiple files. +Possible values are: "single", "multiple"."""), + "plugin_format_flag": attr.string(doc = """ +If provided, this value will be passed to proto-compiler to use the plugin. +The value must contain a single %s which is replaced with plugin executable. +--plugin=protoc-gen-PLUGIN=<executable>."""), + "plugin": attr.label( + executable = True, + cfg = "exec", + doc = """ +If provided, will be made available to the action that calls the proto-compiler, and will be +passed to the proto-compiler: +--plugin=protoc-gen-PLUGIN=<executable>.""", + ), + "runtime": attr.label(doc = """ +A language-specific library that the generated code is compiled against. +The exact behavior is LANG_proto_library-specific. +Java, for example, should compile against the runtime."""), + "blacklisted_protos": attr.label_list( + providers = [ProtoInfo], + doc = """ +No code will be generated for files in the srcs attribute of +blacklisted_protos. +This is used for .proto files that are already linked into proto runtimes, such as +any.proto.""", + ), + # TODO: add doc + "allowlist_different_package": attr.label( + cfg = "exec", + providers = [bazel_features.globals.PackageSpecificationInfo] if bazel_features.globals.PackageSpecificationInfo else [], + ), + # TODO: add doc + "toolchain_type": attr.label(), + # DO NOT USE. For Protobuf incremental changes only: b/305068148. + "protoc_minimal_do_not_use": attr.label( + cfg = "exec", + executable = True, + ), + } | ({} if proto_common.INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION else { + "_proto_compiler": attr.label( + cfg = "exec", + executable = True, + allow_files = True, + default = configuration_field("proto", "proto_compiler"), + ), + }), + provides = [ProtoLangToolchainInfo], + fragments = ["proto"], + toolchains = toolchains.use_toolchain(toolchains.PROTO_TOOLCHAIN), # Used to obtain protoc +) diff --git a/bazel/toolchains/proto_lang_toolchain.bzl b/bazel/toolchains/proto_lang_toolchain.bzl index 7af6537e4d..2b2237f13e 100644 --- a/bazel/toolchains/proto_lang_toolchain.bzl +++ b/bazel/toolchains/proto_lang_toolchain.bzl @@ -1,6 +1,8 @@ """proto_lang_toolchain rule""" +load("@proto_bazel_features//:features.bzl", "bazel_features") load("//bazel/common:proto_common.bzl", "proto_common") +load("//bazel/private:proto_lang_toolchain_rule.bzl", _proto_lang_toolchain_rule = "proto_lang_toolchain") def proto_lang_toolchain(*, name, toolchain_type = None, exec_compatible_with = [], target_compatible_with = [], **attrs): """Creates a proto_lang_toolchain and corresponding toolchain target. @@ -21,8 +23,12 @@ def proto_lang_toolchain(*, name, toolchain_type = None, exec_compatible_with = if getattr(proto_common, "INCOMPATIBLE_PASS_TOOLCHAIN_TYPE", False): attrs["toolchain_type"] = toolchain_type - # buildifier: disable=native-proto - native.proto_lang_toolchain(name = name, **attrs) + # This condition causes Starlark rules to be used only on Bazel >=7.0.0 + if bazel_features.proto.starlark_proto_info: + _proto_lang_toolchain_rule(name = name, **attrs) + else: + # On older Bazel versions keep using native rules, so that mismatch in ProtoInfo doesn't happen + native.proto_lang_toolchain(name = name, **attrs) if toolchain_type: native.toolchain(