Skip to content

Commit 426ca8a

Browse files
Move ProtoInfo and ProtoLangToolchainInfo from Bazel
The change is no-op for Bazel < 8, it always falls back to native providers. When we cherry-pick --incompatible_autoload_externally to older Bazels, ProtoInfo can be replaced with Starlark implementation, providing that users set the flag so that there is no second implementation exposed from Bazel. PiperOrigin-RevId: 674561141
1 parent c87ba1e commit 426ca8a

File tree

14 files changed

+228
-8
lines changed

14 files changed

+228
-8
lines changed

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ bazel_dep(
8585

8686
bazel_dep(
8787
name = "bazel_features",
88-
version = "1.16.0",
88+
version = "1.17.0",
8989
repo_name = "proto_bazel_features",
9090
)
9191

bazel/common/proto_info.bzl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""ProtoInfo"""
22

3-
load("//bazel/private:native.bzl", "NativeProtoInfo")
3+
load("@proto_bazel_features//:features.bzl", "bazel_features")
4+
load("//bazel/private:proto_info.bzl", _ProtoInfo = "ProtoInfo") # buildifier: disable=bzl-visibility
45

5-
ProtoInfo = NativeProtoInfo
6+
# This resolves to Starlark ProtoInfo in Bazel 8 or with --incompatible_enable_autoload flag
7+
ProtoInfo = getattr(bazel_features.globals, "ProtoInfo", None) or _ProtoInfo
Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
"""ProtoLangToolchainInfo"""
22

3-
load("//bazel/private:native.bzl", "native_proto_common")
3+
load("//bazel/private:native.bzl", "native_proto_common") # buildifier: disable=bzl-visibility
44

5-
ProtoLangToolchainInfo = native_proto_common.ProtoLangToolchainInfo
5+
# Use Starlark implementation only if native_proto_common.ProtoLangToolchainInfo doesn't exist
6+
ProtoLangToolchainInfo = getattr(native_proto_common, "ProtoLangToolchainInfo", provider(
7+
doc = """Specifies how to generate language-specific code from .proto files.
8+
Used by LANG_proto_library rules.""",
9+
fields = dict(
10+
out_replacement_format_flag = """(str) Format string used when passing output to the plugin
11+
used by proto compiler.""",
12+
output_files = """("single","multiple","legacy") Format out_replacement_format_flag with
13+
a path to single file or a directory in case of multiple files.""",
14+
plugin_format_flag = "(str) Format string used when passing plugin to proto compiler.",
15+
plugin = "(FilesToRunProvider) Proto compiler plugin.",
16+
runtime = "(Target) Runtime.",
17+
provided_proto_sources = "(list[File]) Proto sources provided by the toolchain.",
18+
proto_compiler = "(FilesToRunProvider) Proto compiler.",
19+
protoc_opts = "(list[str]) Options to pass to proto compiler.",
20+
progress_message = "(str) Progress message to set on the proto compiler action.",
21+
mnemonic = "(str) Mnemonic to set on the proto compiler action.",
22+
allowlist_different_package = """(Target) Allowlist to create lang_proto_library in a
23+
different package than proto_library""",
24+
toolchain_type = """(Label) Toolchain type that was used to obtain this info""",
25+
),
26+
))

bazel/private/native.bzl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
"""Renames toplevel symbols so they can be exported in Starlark under the same name"""
22

3-
NativeProtoInfo = ProtoInfo
4-
53
native_proto_common = proto_common_do_not_use

bazel/private/proto_bazel_features.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ _PROTO_BAZEL_FEATURES = """bazel_features = struct(
1616
),
1717
globals = struct(
1818
PackageSpecificationInfo = {PackageSpecificationInfo},
19+
ProtoInfo = getattr(getattr(native, 'legacy_globals', None), 'ProtoInfo', {ProtoInfo})
1920
),
2021
)
2122
"""
@@ -33,6 +34,7 @@ def _proto_bazel_features_impl(rctx):
3334
PackageSpecificationInfo = major_version_int > 6 or (major_version_int == 6 and minor_version_int >= 4)
3435

3536
protobuf_on_allowlist = major_version_int > 7
37+
ProtoInfo = "ProtoInfo" if major_version_int < 8 else "None"
3638

3739
rctx.file("BUILD.bazel", """
3840
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
@@ -47,6 +49,7 @@ exports_files(["features.bzl"])
4749
starlark_proto_info = repr(starlark_proto_info),
4850
PackageSpecificationInfo = "PackageSpecificationInfo" if PackageSpecificationInfo else "None",
4951
protobuf_on_allowlist = repr(protobuf_on_allowlist),
52+
ProtoInfo = ProtoInfo,
5053
))
5154

5255
proto_bazel_features = repository_rule(

bazel/private/proto_info.bzl

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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+
"""
9+
Definition of ProtoInfo provider.
10+
"""
11+
12+
_warning = """ Don't use this field. It's intended for internal use and will be changed or removed
13+
without warning."""
14+
15+
def _uniq(iterable):
16+
unique_elements = {element: None for element in iterable}
17+
return list(unique_elements.keys())
18+
19+
def _join(*path):
20+
return "/".join([p for p in path if p != ""])
21+
22+
def _empty_to_dot(path):
23+
return path if path else "."
24+
25+
def _from_root(root, repo, relpath):
26+
"""Constructs an exec path from root to relpath"""
27+
if not root:
28+
# `relpath` is a directory with an input source file, the exec path is one of:
29+
# - when in main repo: `package/path`
30+
# - when in a external repository: `external/repo/package/path`
31+
# - with sibling layout: `../repo/package/path`
32+
return _join(repo, relpath)
33+
else:
34+
# `relpath` is a directory with a generated file or an output directory:
35+
# - when in main repo: `{root}/package/path`
36+
# - when in an external repository: `{root}/external/repo/package/path`
37+
# - with sibling layout: `{root}/package/path`
38+
return _join(root, "" if repo.startswith("../") else repo, relpath)
39+
40+
def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace_root = "", bin_dir = None, allow_exports = None):
41+
"""Constructs ProtoInfo.
42+
43+
Args:
44+
srcs: ([File]) List of .proto files (possibly under _virtual path)
45+
deps: ([ProtoInfo]) List of dependencies
46+
descriptor_set: (File) Descriptor set for this Proto
47+
proto_path: (str) Path that should be stripped from files in srcs. When
48+
stripping is needed, the files should be symlinked into `_virtual_imports/target_name`
49+
directory. Only such paths are accepted.
50+
workspace_root: (str) Set to ctx.workspace_root if this is not the main repository.
51+
bin_dir: (str) Set to ctx.bin_dir if _virtual_imports are used.
52+
allow_exports: (Target) The packages where this proto_library can be exported.
53+
54+
Returns:
55+
(ProtoInfo)
56+
"""
57+
58+
# Validate parameters
59+
src_prefix = _join(workspace_root.replace("external/", "../"), proto_path)
60+
for src in srcs:
61+
if type(src) != "File":
62+
fail("srcs parameter expects a list of Files")
63+
if src.owner.workspace_root != workspace_root:
64+
fail("srcs parameter expects all files to have the same workspace_root: ", workspace_root)
65+
if not src.short_path.startswith(src_prefix):
66+
fail("srcs parameter expects all files start with %s" % src_prefix)
67+
if type(descriptor_set) != "File":
68+
fail("descriptor_set parameter expected to be a File")
69+
if proto_path:
70+
if "_virtual_imports/" not in proto_path:
71+
fail("proto_path needs to contain '_virtual_imports' directory")
72+
if proto_path.split("/")[-2] != "_virtual_imports":
73+
fail("proto_path needs to be formed like '_virtual_imports/target_name'")
74+
if not bin_dir:
75+
fail("bin_dir parameter should be set when _virtual_imports are used")
76+
77+
direct_proto_sources = srcs
78+
transitive_proto_sources = depset(
79+
direct = direct_proto_sources,
80+
transitive = [dep._transitive_proto_sources for dep in deps],
81+
order = "preorder",
82+
)
83+
transitive_sources = depset(
84+
direct = srcs,
85+
transitive = [dep.transitive_sources for dep in deps],
86+
order = "preorder",
87+
)
88+
89+
# There can be up more than 1 direct proto_paths, for example when there's
90+
# a generated and non-generated .proto file in srcs
91+
root_paths = _uniq([src.root.path for src in srcs])
92+
transitive_proto_path = depset(
93+
direct = [_empty_to_dot(_from_root(root, workspace_root, proto_path)) for root in root_paths],
94+
transitive = [dep.transitive_proto_path for dep in deps],
95+
)
96+
97+
if srcs:
98+
check_deps_sources = depset(direct = srcs)
99+
else:
100+
check_deps_sources = depset(transitive = [dep.check_deps_sources for dep in deps])
101+
102+
transitive_descriptor_sets = depset(
103+
direct = [descriptor_set],
104+
transitive = [dep.transitive_descriptor_sets for dep in deps],
105+
)
106+
107+
# Layering checks.
108+
if srcs:
109+
exported_sources = depset(direct = direct_proto_sources)
110+
else:
111+
exported_sources = depset(transitive = [dep._exported_sources for dep in deps])
112+
113+
if "_virtual_imports/" in proto_path:
114+
#TODO: remove bin_dir from proto_source_root (when users assuming it's there are migrated)
115+
proto_source_root = _empty_to_dot(_from_root(bin_dir, workspace_root, proto_path))
116+
elif workspace_root.startswith("../"):
117+
proto_source_root = proto_path
118+
else:
119+
proto_source_root = _empty_to_dot(_join(workspace_root, proto_path))
120+
121+
proto_info = dict(
122+
direct_sources = srcs,
123+
transitive_sources = transitive_sources,
124+
direct_descriptor_set = descriptor_set,
125+
transitive_descriptor_sets = transitive_descriptor_sets,
126+
proto_source_root = proto_source_root,
127+
transitive_proto_path = transitive_proto_path,
128+
check_deps_sources = check_deps_sources,
129+
transitive_imports = transitive_sources,
130+
_direct_proto_sources = direct_proto_sources,
131+
_transitive_proto_sources = transitive_proto_sources,
132+
_exported_sources = exported_sources,
133+
)
134+
if allow_exports:
135+
proto_info["allow_exports"] = allow_exports
136+
return proto_info
137+
138+
ProtoInfo, _ = provider(
139+
doc = "Encapsulates information provided by a `proto_library.`",
140+
fields = {
141+
"direct_sources": "(list[File]) The `.proto` source files from the `srcs` attribute.",
142+
"transitive_sources": """(depset[File]) The `.proto` source files from this rule and all
143+
its dependent protocol buffer rules.""",
144+
"direct_descriptor_set": """(File) The descriptor set of the direct sources. If no srcs,
145+
contains an empty file.""",
146+
"transitive_descriptor_sets": """(depset[File]) A set of descriptor set files of all
147+
dependent `proto_library` rules, and this one's. This is not the same as passing
148+
--include_imports to proto-compiler. Will be empty if no dependencies.""",
149+
"proto_source_root": """(str) The directory relative to which the `.proto` files defined in
150+
the `proto_library` are defined. For example, if this is `a/b` and the rule has the
151+
file `a/b/c/d.proto` as a source, that source file would be imported as
152+
`import c/d.proto`
153+
154+
In principle, the `proto_source_root` directory itself should always
155+
be relative to the output directory (`ctx.bin_dir`).
156+
157+
This is at the moment not true for `proto_libraries` using (additional and/or strip)
158+
import prefixes. `proto_source_root` is in this case prefixed with the output
159+
directory. For example, the value is similar to
160+
`bazel-out/k8-fastbuild/bin/a/_virtual_includes/b` for an input file in
161+
`a/_virtual_includes/b/c.proto` that should be imported as `c.proto`.
162+
163+
When using the value please account for both cases in a general way.
164+
That is assume the value is either prefixed with the output directory or not.
165+
This will make it possible to fix `proto_library` in the future.
166+
""",
167+
"transitive_proto_path": """(depset(str) A set of `proto_source_root`s collected from the
168+
transitive closure of this rule.""",
169+
"check_deps_sources": """(depset[File]) The `.proto` sources from the 'srcs' attribute.
170+
If the library is a proxy library that has no sources, it contains the
171+
`check_deps_sources` from this library's direct deps.""",
172+
"allow_exports": """(Target) The packages where this proto_library can be exported.""",
173+
174+
# Deprecated fields:
175+
"transitive_imports": """(depset[File]) Deprecated: use `transitive_sources` instead.""",
176+
177+
# Internal fields:
178+
"_direct_proto_sources": """(list[File]) The `ProtoSourceInfo`s from the `srcs`
179+
attribute.""" + _warning,
180+
"_transitive_proto_sources": """(depset[File]) The `ProtoSourceInfo`s from this
181+
rule and all its dependent protocol buffer rules.""" + _warning,
182+
"_exported_sources": """(depset[File]) A set of `ProtoSourceInfo`s that may be
183+
imported by another `proto_library` depending on this one.""" + _warning,
184+
},
185+
init = _create_proto_info,
186+
)

bazel/tests/testdata/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
load("//bazel/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain")
2+
13
package(
24
default_applicable_licenses = ["//:license"],
35
default_visibility = ["//visibility:public"],

build_defs/java_opts.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ BUNDLE_LICENSE = "https://opensource.org/licenses/BSD-3-Clause"
1717
def protobuf_java_export(**kwargs):
1818
java_export(
1919
javacopts = JAVA_OPTS,
20+
# https://github.com/bazelbuild/rules_jvm_external/issues/1245
2021
javadocopts = [
2122
"-notimestamp",
2223
"-use",

conformance/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ load("//:protobuf.bzl", "internal_csharp_proto_library", "internal_objc_proto_li
1212
load("//bazel:cc_proto_library.bzl", "cc_proto_library")
1313
load("//bazel:java_lite_proto_library.bzl", "java_lite_proto_library")
1414
load("//bazel:java_proto_library.bzl", "java_proto_library")
15+
load("//bazel:proto_library.bzl", "proto_library")
1516
load("//build_defs:internal_shell.bzl", "inline_sh_binary")
1617
load("//ruby:defs.bzl", "internal_ruby_proto_library")
1718

conformance/test_protos/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
load("@rules_cc//cc:defs.bzl", "cc_proto_library", "objc_library")
1+
load("@rules_cc//cc:defs.bzl", "objc_library")
22
load("//:protobuf.bzl", "internal_csharp_proto_library", "internal_objc_proto_library", "internal_py_proto_library")
33
load("//bazel:cc_proto_library.bzl", "cc_proto_library")
44
load("//bazel:java_lite_proto_library.bzl", "java_lite_proto_library")
55
load("//bazel:java_proto_library.bzl", "java_proto_library")
6+
load("//bazel:proto_library.bzl", "proto_library")
67
load("//ruby:defs.bzl", "internal_ruby_proto_library")
78

89
package(

0 commit comments

Comments
 (0)