1# Protocol Buffers - Google's data interchange format 2# Copyright 2024 Google Inc. All rights reserved. 3# 4# Use of this source code is governed by a BSD-style 5# license that can be found in the LICENSE file or at 6# https://developers.google.com/open-source/licenses/bsd 7# 8"""Implementation of the proto_lang_toolchain rule.""" 9 10load("@proto_bazel_features//:features.bzl", "bazel_features") 11load("//bazel/common:proto_common.bzl", "proto_common") 12load("//bazel/common:proto_info.bzl", "ProtoInfo") 13load("//bazel/common:proto_lang_toolchain_info.bzl", "ProtoLangToolchainInfo") 14load("//bazel/private:toolchain_helpers.bzl", "toolchains") 15 16def _rule_impl(ctx): 17 provided_proto_sources = depset(transitive = [bp[ProtoInfo]._transitive_proto_sources for bp in ctx.attr.blacklisted_protos]).to_list() 18 19 flag = ctx.attr.command_line 20 if flag.find("$(PLUGIN_OUT)") > -1: 21 fail("in attribute 'command_line': Placeholder '$(PLUGIN_OUT)' is not supported.") 22 flag = flag.replace("$(OUT)", "%s") 23 24 plugin = None 25 if ctx.attr.plugin != None: 26 plugin = ctx.attr.plugin[DefaultInfo].files_to_run 27 28 if proto_common.INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION: 29 proto_compiler = ctx.toolchains[toolchains.PROTO_TOOLCHAIN].proto.proto_compiler 30 protoc_opts = ctx.toolchains[toolchains.PROTO_TOOLCHAIN].proto.protoc_opts 31 else: 32 proto_compiler = ctx.attr._proto_compiler.files_to_run 33 protoc_opts = ctx.fragments.proto.experimental_protoc_opts 34 35 if ctx.attr.protoc_minimal_do_not_use: 36 proto_compiler = ctx.attr.protoc_minimal_do_not_use.files_to_run 37 38 proto_lang_toolchain_info = ProtoLangToolchainInfo( 39 out_replacement_format_flag = flag, 40 output_files = ctx.attr.output_files, 41 plugin_format_flag = ctx.attr.plugin_format_flag, 42 plugin = plugin, 43 runtime = ctx.attr.runtime, 44 provided_proto_sources = provided_proto_sources, 45 proto_compiler = proto_compiler, 46 protoc_opts = protoc_opts, 47 progress_message = ctx.attr.progress_message, 48 mnemonic = ctx.attr.mnemonic, 49 allowlist_different_package = ctx.attr.allowlist_different_package, 50 toolchain_type = ctx.attr.toolchain_type.label if ctx.attr.toolchain_type else None, 51 ) 52 return [ 53 DefaultInfo(files = depset(), runfiles = ctx.runfiles()), 54 platform_common.ToolchainInfo(proto = proto_lang_toolchain_info), 55 # TODO: remove when --incompatible_enable_proto_toolchains is flipped and removed 56 proto_lang_toolchain_info, 57 ] 58 59proto_lang_toolchain = rule( 60 _rule_impl, 61 doc = """ 62<p>If using Bazel, please load the rule from <a href="https://github.com/bazelbuild/rules_proto"> 63https://github.com/bazelbuild/rules_proto</a>. 64 65<p>Specifies how a LANG_proto_library rule (e.g., <code>java_proto_library</code>) should invoke the 66proto-compiler. 67Some LANG_proto_library rules allow specifying which toolchain to use using command-line flags; 68consult their documentation. 69 70<p>Normally you should not write those kind of rules unless you want to 71tune your Java compiler. 72 73<p>There's no compiler. The proto-compiler is taken from the proto_library rule we attach to. It is 74passed as a command-line flag to Blaze. 75Several features require a proto-compiler to be invoked on the proto_library rule itself. 76It's beneficial to enforce the compiler that LANG_proto_library uses is the same as the one 77<code>proto_library</code> does. 78 79<h4>Examples</h4> 80 81<p>A simple example would be: 82<pre><code class="lang-starlark"> 83proto_lang_toolchain( 84 name = "javalite_toolchain", 85 command_line = "--javalite_out=shared,immutable:$(OUT)", 86 plugin = ":javalite_plugin", 87 runtime = ":protobuf_lite", 88) 89</code></pre> 90 """, 91 attrs = { 92 "progress_message": attr.string(default = "Generating proto_library %{label}", doc = """ 93This value will be set as the progress message on protoc action."""), 94 "mnemonic": attr.string(default = "GenProto", doc = """ 95This value will be set as the mnemonic on protoc action."""), 96 "command_line": attr.string(mandatory = True, doc = """ 97This value will be passed to proto-compiler to generate the code. Only include the parts 98specific to this code-generator/plugin (e.g., do not include -I parameters) 99<ul> 100 <li><code>$(OUT)</code> is LANG_proto_library-specific. The rules are expected to define 101 how they interpret this variable. For Java, for example, $(OUT) will be replaced with 102 the src-jar filename to create.</li> 103</ul>"""), 104 "output_files": attr.string(values = ["single", "multiple", "legacy"], default = "legacy", doc = """ 105Controls how <code>$(OUT)</code> in <code>command_line</code> is formatted, either by 106a path to a single file or output directory in case of multiple files. 107Possible values are: "single", "multiple"."""), 108 "plugin_format_flag": attr.string(doc = """ 109If provided, this value will be passed to proto-compiler to use the plugin. 110The value must contain a single %s which is replaced with plugin executable. 111<code>--plugin=protoc-gen-PLUGIN=<executable>.</code>"""), 112 "plugin": attr.label( 113 executable = True, 114 cfg = "exec", 115 doc = """ 116If provided, will be made available to the action that calls the proto-compiler, and will be 117passed to the proto-compiler: 118<code>--plugin=protoc-gen-PLUGIN=<executable>.</code>""", 119 ), 120 "runtime": attr.label(doc = """ 121A language-specific library that the generated code is compiled against. 122The exact behavior is LANG_proto_library-specific. 123Java, for example, should compile against the runtime."""), 124 "blacklisted_protos": attr.label_list( 125 providers = [ProtoInfo], 126 doc = """ 127No code will be generated for files in the <code>srcs</code> attribute of 128<code>blacklisted_protos</code>. 129This is used for .proto files that are already linked into proto runtimes, such as 130<code>any.proto</code>.""", 131 ), 132 # TODO: add doc 133 "allowlist_different_package": attr.label( 134 cfg = "exec", 135 providers = [bazel_features.globals.PackageSpecificationInfo] if bazel_features.globals.PackageSpecificationInfo else [], 136 ), 137 # TODO: add doc 138 "toolchain_type": attr.label(), 139 # DO NOT USE. For Protobuf incremental changes only: b/305068148. 140 "protoc_minimal_do_not_use": attr.label( 141 cfg = "exec", 142 executable = True, 143 ), 144 } | ({} if proto_common.INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION else { 145 "_proto_compiler": attr.label( 146 cfg = "exec", 147 executable = True, 148 allow_files = True, 149 default = configuration_field("proto", "proto_compiler"), 150 ), 151 }), 152 provides = [ProtoLangToolchainInfo], 153 fragments = ["proto"], 154 toolchains = toolchains.use_toolchain(toolchains.PROTO_TOOLCHAIN), # Used to obtain protoc 155) 156