blob: b41888376a2d7eeb55c2664f0fa70a9cb0802f3a [file] [log] [blame]
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load(
"//go:def.bzl",
"GoLibrary",
"GoSource",
"go_context",
)
load(
"@bazel_skylib//lib:types.bzl",
"types",
)
load(
"//proto:compiler.bzl",
"GoProtoCompiler",
"proto_path",
)
load(
"//go/private:go_toolchain.bzl",
"GO_TOOLCHAIN",
)
load(
"//go/private:providers.bzl",
"INFERRED_PATH",
)
load(
"//go/private/rules:transition.bzl",
"non_go_tool_transition",
)
load(
"@rules_proto//proto:defs.bzl",
"ProtoInfo",
)
GoProtoImports = provider()
def get_imports(attr):
proto_deps = []
# ctx.attr.proto is a one-element array since there is a Starlark transition attached to it.
if hasattr(attr, "proto") and attr.proto and types.is_list(attr.proto) and ProtoInfo in attr.proto[0]:
proto_deps = [attr.proto[0]]
elif hasattr(attr, "protos"):
proto_deps = [d for d in attr.protos if ProtoInfo in d]
else:
proto_deps = []
direct = dict()
for dep in proto_deps:
for src in dep[ProtoInfo].check_deps_sources.to_list():
direct["{}={}".format(proto_path(src, dep[ProtoInfo]), attr.importpath)] = True
deps = getattr(attr, "deps", []) + getattr(attr, "embed", [])
transitive = [
dep[GoProtoImports].imports
for dep in deps
if GoProtoImports in dep
]
return depset(direct = direct.keys(), transitive = transitive)
def _go_proto_aspect_impl(target, ctx):
imports = get_imports(ctx.rule.attr)
return [GoProtoImports(imports = imports)]
_go_proto_aspect = aspect(
_go_proto_aspect_impl,
attr_aspects = [
"deps",
"embed",
],
)
def _proto_library_to_source(go, attr, source, merge):
if attr.compiler:
compilers = [attr.compiler]
else:
compilers = attr.compilers
for compiler in compilers:
if GoSource in compiler:
merge(source, compiler[GoSource])
def _go_proto_library_impl(ctx):
go = go_context(ctx)
if go.pathtype == INFERRED_PATH:
fail("importpath must be specified in this library or one of its embedded libraries")
if ctx.attr.compiler:
#TODO: print("DEPRECATED: compiler attribute on {}, use compilers instead".format(ctx.label))
compilers = [ctx.attr.compiler]
else:
compilers = ctx.attr.compilers
if ctx.attr.proto:
#TODO: print("DEPRECATED: proto attribute on {}, use protos instead".format(ctx.label))
if ctx.attr.protos:
fail("Either proto or protos (non-empty) argument must be specified, but not both")
# ctx.attr.proto is a one-element array since there is a Starlark transition attached to it.
proto_deps = [ctx.attr.proto[0]]
else:
if not ctx.attr.protos:
fail("Either proto or protos (non-empty) argument must be specified")
proto_deps = ctx.attr.protos
go_srcs = []
valid_archive = False
for c in compilers:
compiler = c[GoProtoCompiler]
if compiler.valid_archive:
valid_archive = True
go_srcs.extend(compiler.compile(
go,
compiler = compiler,
protos = [d[ProtoInfo] for d in proto_deps],
imports = get_imports(ctx.attr),
importpath = go.importpath,
))
library = go.new_library(
go,
resolver = _proto_library_to_source,
srcs = go_srcs,
)
source = go.library_to_source(go, ctx.attr, library, False)
providers = [library, source]
output_groups = {
"go_generated_srcs": go_srcs,
}
if valid_archive:
archive = go.archive(go, source)
output_groups["compilation_outputs"] = [archive.data.file]
providers.extend([
archive,
DefaultInfo(
files = depset([archive.data.file]),
runfiles = archive.runfiles,
),
])
return providers + [OutputGroupInfo(**output_groups)]
go_proto_library = rule(
implementation = _go_proto_library_impl,
attrs = {
"proto": attr.label(
cfg = non_go_tool_transition,
providers = [ProtoInfo],
),
"protos": attr.label_list(
cfg = non_go_tool_transition,
providers = [ProtoInfo],
default = [],
),
"deps": attr.label_list(
providers = [GoLibrary],
aspects = [_go_proto_aspect],
),
"importpath": attr.string(),
"importmap": attr.string(),
"importpath_aliases": attr.string_list(), # experimental, undocumented
"embed": attr.label_list(providers = [GoLibrary]),
"gc_goopts": attr.string_list(),
"compiler": attr.label(providers = [GoProtoCompiler]),
"compilers": attr.label_list(
providers = [GoProtoCompiler],
default = ["//proto:go_proto"],
),
"_go_context_data": attr.label(
default = "//:go_context_data",
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
toolchains = [GO_TOOLCHAIN],
)
# go_proto_library is a rule that takes a proto_library (in the proto
# attribute) and produces a go library for it.
def go_grpc_library(**kwargs):
# TODO: Deprecate once gazelle generates just go_proto_library
go_proto_library(compilers = [Label("//proto:go_grpc")], **kwargs)
def proto_register_toolchains():
print("You no longer need to call proto_register_toolchains(), it does nothing")