1# Copyright 2017 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15load( 16 "//go:def.bzl", 17 "GoLibrary", 18 "GoSource", 19 "go_context", 20) 21load( 22 "@bazel_skylib//lib:types.bzl", 23 "types", 24) 25load( 26 "//proto:compiler.bzl", 27 "GoProtoCompiler", 28 "proto_path", 29) 30load( 31 "//go/private:go_toolchain.bzl", 32 "GO_TOOLCHAIN", 33) 34load( 35 "//go/private:providers.bzl", 36 "INFERRED_PATH", 37) 38load( 39 "//go/private/rules:transition.bzl", 40 "non_go_tool_transition", 41) 42load( 43 "@rules_proto//proto:defs.bzl", 44 "ProtoInfo", 45) 46 47GoProtoImports = provider() 48 49def get_imports(attr): 50 proto_deps = [] 51 52 # ctx.attr.proto is a one-element array since there is a Starlark transition attached to it. 53 if hasattr(attr, "proto") and attr.proto and types.is_list(attr.proto) and ProtoInfo in attr.proto[0]: 54 proto_deps = [attr.proto[0]] 55 elif hasattr(attr, "protos"): 56 proto_deps = [d for d in attr.protos if ProtoInfo in d] 57 else: 58 proto_deps = [] 59 60 direct = dict() 61 for dep in proto_deps: 62 for src in dep[ProtoInfo].check_deps_sources.to_list(): 63 direct["{}={}".format(proto_path(src, dep[ProtoInfo]), attr.importpath)] = True 64 65 deps = getattr(attr, "deps", []) + getattr(attr, "embed", []) 66 transitive = [ 67 dep[GoProtoImports].imports 68 for dep in deps 69 if GoProtoImports in dep 70 ] 71 return depset(direct = direct.keys(), transitive = transitive) 72 73def _go_proto_aspect_impl(_target, ctx): 74 imports = get_imports(ctx.rule.attr) 75 return [GoProtoImports(imports = imports)] 76 77_go_proto_aspect = aspect( 78 _go_proto_aspect_impl, 79 attr_aspects = [ 80 "deps", 81 "embed", 82 ], 83) 84 85def _proto_library_to_source(_go, attr, source, merge): 86 if attr.compiler: 87 compilers = [attr.compiler] 88 else: 89 compilers = attr.compilers 90 for compiler in compilers: 91 if GoSource in compiler: 92 merge(source, compiler[GoSource]) 93 94def _go_proto_library_impl(ctx): 95 go = go_context(ctx) 96 if go.pathtype == INFERRED_PATH: 97 fail("importpath must be specified in this library or one of its embedded libraries") 98 if ctx.attr.compiler: 99 #TODO: print("DEPRECATED: compiler attribute on {}, use compilers instead".format(ctx.label)) 100 compilers = [ctx.attr.compiler] 101 else: 102 compilers = ctx.attr.compilers 103 104 if ctx.attr.proto: 105 #TODO: print("DEPRECATED: proto attribute on {}, use protos instead".format(ctx.label)) 106 if ctx.attr.protos: 107 fail("Either proto or protos (non-empty) argument must be specified, but not both") 108 109 # ctx.attr.proto is a one-element array since there is a Starlark transition attached to it. 110 proto_deps = [ctx.attr.proto[0]] 111 else: 112 if not ctx.attr.protos: 113 fail("Either proto or protos (non-empty) argument must be specified") 114 proto_deps = ctx.attr.protos 115 116 go_srcs = [] 117 valid_archive = False 118 119 for c in compilers: 120 compiler = c[GoProtoCompiler] 121 if compiler.valid_archive: 122 valid_archive = True 123 go_srcs.extend(compiler.compile( 124 go, 125 compiler = compiler, 126 protos = [d[ProtoInfo] for d in proto_deps], 127 imports = get_imports(ctx.attr), 128 importpath = go.importpath, 129 )) 130 library = go.new_library( 131 go, 132 resolver = _proto_library_to_source, 133 srcs = go_srcs, 134 ) 135 source = go.library_to_source(go, ctx.attr, library, False) 136 providers = [library, source] 137 output_groups = { 138 "go_generated_srcs": go_srcs, 139 } 140 if valid_archive: 141 archive = go.archive(go, source) 142 output_groups["compilation_outputs"] = [archive.data.file] 143 providers.extend([ 144 archive, 145 DefaultInfo( 146 files = depset([archive.data.file]), 147 runfiles = archive.runfiles, 148 ), 149 ]) 150 return providers + [OutputGroupInfo(**output_groups)] 151 152go_proto_library = rule( 153 implementation = _go_proto_library_impl, 154 attrs = { 155 "proto": attr.label( 156 cfg = non_go_tool_transition, 157 providers = [ProtoInfo], 158 ), 159 "protos": attr.label_list( 160 cfg = non_go_tool_transition, 161 providers = [ProtoInfo], 162 default = [], 163 ), 164 "deps": attr.label_list( 165 providers = [GoLibrary], 166 aspects = [_go_proto_aspect], 167 ), 168 "importpath": attr.string(), 169 "importmap": attr.string(), 170 "importpath_aliases": attr.string_list(), # experimental, undocumented 171 "embed": attr.label_list(providers = [GoLibrary]), 172 "gc_goopts": attr.string_list(), 173 "compiler": attr.label(providers = [GoProtoCompiler]), 174 "compilers": attr.label_list( 175 providers = [GoProtoCompiler], 176 default = ["//proto:go_proto"], 177 ), 178 "_go_context_data": attr.label( 179 default = "//:go_context_data", 180 ), 181 "_allowlist_function_transition": attr.label( 182 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 183 ), 184 }, 185 toolchains = [GO_TOOLCHAIN], 186) 187# go_proto_library is a rule that takes a proto_library (in the proto 188# attribute) and produces a go library for it. 189 190def go_grpc_library(**kwargs): 191 # TODO: Deprecate once gazelle generates just go_proto_library 192 go_proto_library(compilers = [Label("//proto:go_grpc")], **kwargs) 193 194def proto_register_toolchains(): 195 print("You no longer need to call proto_register_toolchains(), it does nothing") 196