Skip to content

Commit

Permalink
Use aspect to infer deps automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
mering committed Sep 21, 2023
1 parent 7309aba commit 02661f2
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 125 deletions.
5 changes: 4 additions & 1 deletion go/private/context.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ def _library_to_source(go, attr, library, coverage_instrumented):
generated_srcs = getattr(library, "srcs", [])
srcs = attr_srcs + generated_srcs
embedsrcs = [f for t in getattr(attr, "embedsrcs", []) for f in as_iterable(t.files)]
attr_deps = getattr(attr, "deps", [])
generated_deps = getattr(library, "deps", [])
deps = attr_deps + generated_deps
source = {
"library": library,
"mode": go.mode,
Expand All @@ -257,7 +260,7 @@ def _library_to_source(go, attr, library, coverage_instrumented):
"cover": [],
"embedsrcs": embedsrcs,
"x_defs": {},
"deps": getattr(attr, "deps", []),
"deps": deps,
"gc_goopts": _expand_opts(go, "gc_goopts", getattr(attr, "gc_goopts", [])),
"runfiles": _collect_runfiles(go, getattr(attr, "data", []), getattr(attr, "deps", [])),
"cgo": getattr(attr, "cgo", False),
Expand Down
189 changes: 65 additions & 124 deletions proto/def.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@

load(
"//go:def.bzl",
"GoArchive",
"GoLibrary",
"GoSource",
"go_context",
)
load(
"@bazel_skylib//lib:types.bzl",
"types",
)
load(
"//proto:compiler.bzl",
"GoProtoCompiler",
Expand All @@ -31,165 +28,109 @@ 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 = []

def get_imports(target, attr, importpath):
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
for src in target[ProtoInfo].check_deps_sources.to_list():
direct["{}={}".format(proto_path(src, target[ProtoInfo]), importpath)] = True

deps = getattr(attr, "deps", []) + getattr(attr, "embed", [])
transitive = [
dep[GoProtoImports].imports
for dep in deps
for dep in getattr(attr, "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])
compiler = attr._compiler
if GoSource in compiler:
merge(source, compiler[GoSource])

def _go_proto_library_impl(ctx):
def _go_proto_aspect_impl(target, 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
proto_info = target[ProtoInfo]
imports = get_imports(target, ctx.rule.attr, go.importpath)
if proto_info.direct_sources:
compiler = ctx.attr._compiler[GoProtoCompiler]
go_srcs.extend(compiler.compile(
go,
compiler = compiler,
protos = [d[ProtoInfo] for d in proto_deps],
imports = get_imports(ctx.attr),
protos = [proto_info],
imports = imports,
importpath = go.importpath,
))

go_deps = getattr(ctx.rule.attr, "deps", [])
library = go.new_library(
go,
resolver = _proto_library_to_source,
srcs = go_srcs,
deps = go_deps,
)
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)]
archive = go.archive(go, source)

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],
return [
GoProtoImports(imports = imports),
OutputGroupInfo(
go_generated_srcs = go_srcs,
compilation_outputs = [archive.data.file],
),
"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"],
library,
source,
archive,
]

_go_proto_aspect = aspect(
_go_proto_aspect_impl,
attrs = {
"_compiler": attr.label(
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",
),
},
attr_aspects = [
"deps",
],
required_providers = [ProtoInfo],
provides = [
GoLibrary,
GoSource,
GoArchive,
],
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 _go_proto_library_impl(ctx):
if not ctx.attr.deps:
fail("'deps' attribute mustn't be empty.")

providers = []
for dep in ctx.attr.deps:
for provider in [GoLibrary, GoSource, GoArchive]:
if provider in dep:
providers.append(dep[provider])

def proto_register_toolchains():
print("You no longer need to call proto_register_toolchains(), it does nothing")
return providers

go_proto_library = rule(
implementation = _go_proto_library_impl,
attrs = {
"deps": attr.label_list(
providers = [ProtoInfo],
aspects = [_go_proto_aspect],
),
},
)
# go_proto_library is a rule that takes a proto_library (in the proto
# attribute) and produces a go library for it.

0 comments on commit 02661f2

Please sign in to comment.