Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use aspect to infer proto deps automatically #3706

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ go_deps.module(
sum = "h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=",
version = "v1.3.2",
)

# TODO Request addition to DEFAULT_BUILD_FILE_GENERATION_BY_PATH in https://github.com/bazelbuild/bazel-gazelle/tree/master/internal/bzlmod/default_gazelle_overrides.bzl
# go_deps.gazelle_override(
# path = "google.golang.org/grpc",
# build_file_generation = "on",
# )
# go_deps.module_override(
# path = "google.golang.org/grpc",
# patches = ["//third_party:com_github_golang_protobuf-gazelle.patch"],
# patch_strip = 1,
# )
use_repo(
go_deps,
"com_github_gogo_protobuf",
Expand Down
5 changes: 5 additions & 0 deletions go/private/context.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ load(
"GoContextInfo",
"GoLibrary",
"GoSource",
"GoProtoInfo",
"GoStdLib",
"INFERRED_PATH",
"get_source",
Expand Down Expand Up @@ -231,6 +232,8 @@ def _dedup_deps(deps):
for dep in deps:
if hasattr(dep, "data") and hasattr(dep.data, "importpath"):
importpath = dep.data.importpath
elif GoProtoInfo in dep:
importpath = dep[GoProtoInfo].library.importpath
else:
importpath = dep[GoLibrary].importpath
if importpath in importpaths:
Expand Down Expand Up @@ -321,6 +324,8 @@ def _collect_cc_infos(deps, cdeps):
# dep may be a struct, which doesn't support indexing by providers.
if is_struct(dep):
continue
if GoProtoInfo in dep:
cc_infos.append(dep[GoProtoInfo].source.cc_info)
if GoSource in dep:
cc_infos.append(dep[GoSource].cc_info)
return cc_common.merge_cc_infos(cc_infos = cc_infos)
Expand Down
13 changes: 13 additions & 0 deletions go/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ GoArchiveData = provider()
# See go/providers.rst#GoArchive for full documentation.
GoArchive = provider()

GoProtoInfo = provider(
doc = "Info generated by the go_proto_library aspect.",
fields = {
"library": "Generated GoLibrary",
"source": "Generated GoSource",
"archive": "Generated GoArchive",
},
)

GoPath = provider()

GoSDK = provider(
Expand Down Expand Up @@ -76,11 +85,15 @@ EXPORT_PATH = "export"
def get_source(dep):
if type(dep) == "struct":
return dep
if GoProtoInfo in dep:
return dep[GoProtoInfo].source
return dep[GoSource]

def get_archive(dep):
if type(dep) == "struct":
return dep
if GoProtoInfo in dep:
return dep[GoProtoInfo].archive
return dep[GoArchive]

def effective_importpath_pkgpath(lib):
Expand Down
20 changes: 18 additions & 2 deletions go/private/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,25 @@ def go_rules_dependencies(force = False):
patch_args = ["-p1"],
)

# releaser:upgrade-dep grpc grpc-go
wrapper(
http_archive,
name = "org_golang_google_grpc",
# v1.59.0, latest as of 2023-11-24
urls = [
"https://mirror.bazel.build/github.com/grpc/grpc-go/archive/refs/tags/v1.59.0.zip",
"https://github.com/grpc/grpc-go/archive/refs/tags/v1.59.0.zip",
],
sha256 = "70b184f6deb7beb99e91777a5ec062b1c37de5d6310864e214b0ec54f7c4728f",
strip_prefix = "grpc-go-1.59.0",
patches = [
# releaser:patch-cmd gazelle -repo_root . -go_prefix google.golang.org/grpc -go_naming_convention import_alias -proto disable_global
Label("//third_party:org_golang_google_grpc-gazelle.patch"),
],
patch_args = ["-p1"],
)

# Legacy protobuf compiler, runtime, and utilities.
# We still use protoc-gen-go because the new one doesn't support gRPC, and
# the gRPC compiler doesn't exist yet.
# We need to apply a patch to enable both go_proto_library and
# go_library with pre-generated sources.
# releaser:upgrade-dep golang protobuf
Expand Down
5 changes: 4 additions & 1 deletion proto/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ go_proto_compiler(

go_proto_compiler(
name = "go_proto",
plugin = "@org_golang_google_protobuf//cmd/protoc-gen-go",
visibility = ["//visibility:public"],
deps = PROTO_RUNTIME_DEPS + WELL_KNOWN_TYPES_APIV2,
)

go_proto_compiler(
name = "go_grpc",
options = ["plugins=grpc"],
options = ["require_unimplemented_servers=false"],
plugin = "@org_golang_google_grpc//cmd/protoc-gen-go-grpc",
suffix = "_grpc.pb.go",
visibility = ["//visibility:public"],
deps = PROTO_RUNTIME_DEPS + WELL_KNOWN_TYPES_APIV2 + [
"@org_golang_google_grpc//:go_default_library",
Expand Down
21 changes: 18 additions & 3 deletions proto/def.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,24 @@ go_proto_library = rule(
# 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_grpc_library(name, importpath = None, **kwargs):
# Importpath needs to be the same for embed to work correctly.
# This is roughly equivalent to _infer_importpath() from rules_go/go/private/context.bzl
if not importpath:
importpath = "%s/%s" % (native.package_name(), name)

go_proto_library(
name = name + "_proto",
importpath = importpath,
**kwargs
)
go_proto_library(
name = name,
compilers = [Label("//proto:go_grpc")],
embed = [":" + name + "_proto"],
importpath = importpath,
**kwargs
)

def proto_register_toolchains():
print("You no longer need to call proto_register_toolchains(), it does nothing")
185 changes: 185 additions & 0 deletions proto/def_v2.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# 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",
"GoArchive",
"GoLibrary",
"GoSource",
"go_context",
)
load(
"//proto:compiler.bzl",
"GoProtoCompiler",
"proto_path",
)
load(
"//go/private:common.bzl",
"GO_TOOLCHAIN",
)
load(
"@rules_proto//proto:defs.bzl",
"ProtoInfo",
)
load(
"//go/private:providers.bzl",
"GoProtoInfo",
)

GoProtoImports = provider()

def get_imports(target, attr, importpath):
direct = dict()
for src in target[ProtoInfo].check_deps_sources.to_list():
direct["{}={}".format(proto_path(src, target[ProtoInfo]), importpath)] = True

transitive = [
dep[GoProtoImports].imports
for dep in getattr(attr, "deps", [])
if GoProtoImports in dep
]
return depset(direct = direct.keys(), transitive = transitive)

def _library_to_source(_go, attr, source, merge):
compiler = attr._compiler
if GoSource in compiler:
merge(source, compiler[GoSource])

def _compile_proto(ctx, target, attr):
go = go_context(ctx)
go_srcs = []
proto_info = target[ProtoInfo]
imports = get_imports(target, attr, go.importpath)
compiler = ctx.attr._compiler[GoProtoCompiler]
if proto_info.direct_sources:
go_srcs.extend(compiler.compile(
go,
compiler = compiler,
protos = [proto_info],
imports = imports,
importpath = go.importpath,
))

go_deps = getattr(attr, "deps", [])
library = go.new_library(
go,
resolver = _library_to_source,
srcs = go_srcs,
deps = go_deps + compiler.deps,
)
source = go.library_to_source(go, ctx.attr, library, False)
archive = go.archive(go, source)
return {
"imports": GoProtoImports(
imports = imports,
),
"info": GoProtoInfo(
library = library,
source = source,
archive = archive,
),
}

def _go_proto_aspect_impl(target, ctx):
providers = _compile_proto(ctx, target, ctx.rule.attr)

return [
providers["imports"],
providers["info"],
]

_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",
),
},
attr_aspects = [
"deps",
],
required_providers = [ProtoInfo],
provides = [GoProtoImports, GoProtoInfo],
toolchains = [GO_TOOLCHAIN],
)

def _go_proto_library_impl(ctx):
if len(ctx.attr.protos) != 1:
fail("'protos' attribute must contain exactly one element. Got %s." % len(ctx.attr.protos))
proto = ctx.attr.protos[0]

return [
proto[GoProtoImports],
proto[GoProtoInfo].library,
proto[GoProtoInfo].source,
proto[GoProtoInfo].archive,
]

go_proto_library = rule(
implementation = _go_proto_library_impl,
attrs = {
"protos": attr.label_list(
providers = [ProtoInfo],
aspects = [_go_proto_aspect],
mandatory = True,
),
},
provides = [
GoProtoImports, # This is used by go_grpc_library to determine importpaths for proto deps
GoLibrary,
GoSource,
GoArchive,
],
)

def _go_grpc_library_impl(ctx):
if len(ctx.attr.protos) != 1:
fail("protos attribute must be exactly one target")

providers = _compile_proto(ctx, ctx.attr.protos[0], ctx.attr)

return [
providers["info"].library,
providers["info"].source,
providers["info"].archive,
]

go_grpc_library = rule(
implementation = _go_grpc_library_impl,
attrs = {
"protos": attr.label_list(
providers = [ProtoInfo],
mandatory = True,
),
"deps": attr.label_list(
providers = [GoLibrary],
),
"importpath": attr.string(),
"_compiler": attr.label(
default = "//proto:go_grpc",
),
"_go_context_data": attr.label(
default = "//:go_context_data",
),
},
provides = [
GoLibrary,
GoSource,
GoArchive,
],
toolchains = [GO_TOOLCHAIN],
)
1 change: 1 addition & 0 deletions proto/wkt/well_known_types.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PROTO_RUNTIME_DEPS = [
"@com_github_golang_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//reflect/protoregistry:go_default_library",
"@org_golang_google_protobuf//runtime/protoiface:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
]
Expand Down
5 changes: 2 additions & 3 deletions tests/integration/googleapis/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_grpc_library")
load("@rules_proto//proto:defs.bzl", "proto_library")

proto_library(
Expand All @@ -11,9 +11,8 @@ proto_library(
],
)

go_proto_library(
go_grpc_library(
name = "color_service_go_proto",
compilers = ["@io_bazel_rules_go//proto:go_grpc"],
importpath = "github.com/bazelbuild/rules_go/tests/integration/googleapis/color_service_proto",
proto = ":color_service_proto",
deps = [
Expand Down