Skip to content

Commit aebaf25

Browse files
zhangskzcopybara-github
authored andcommitted
Add option_deps to proto_library for import option.
`option_deps` are passed to protoc as `--option_dependencies` for resolution of option imports, but should not be included in the outputted descriptor set or considered by <lang>_proto_library aspects (i.e. `attr_aspects`) for gencode output. PiperOrigin-RevId: 743679882
1 parent 469ca55 commit aebaf25

File tree

6 files changed

+73
-20
lines changed

6 files changed

+73
-20
lines changed

.github/workflows/test_bazel.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ jobs:
2727
fail-fast: false
2828
matrix:
2929
runner: [ ubuntu, windows, macos ]
30+
# Bazel < 7.3.0 depends on rules_proto version that doesn't have @rules_proto//proto:toolchain_type target used by Bazel
31+
# https://github.com/bazelbuild/bazel/blob/a82541b93897216ec9c0ab48eaa8fd95c2995864/src/main/starlark/builtins_bzl/common/proto/proto_semantics.bzl#L20
3032
bazelversion: [ '7.1.2', '8.0.0' ]
3133
bzlmod: [ true, false ]
3234
toolchain_resolution: [ "", "--incompatible_enable_proto_toolchain_resolution=true" ]
@@ -67,7 +69,7 @@ jobs:
6769
uses: protocolbuffers/protobuf-ci/bazel@v4
6870
with:
6971
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
70-
bazel-cache: examples
72+
bazel-cache: examples-${{ matrix.bazelversion }}-${{ matrix.bzlmod }}-${{ matrix.toolchain_resolution }}
7173
version: ${{ matrix.bazelversion }}
7274
bash: >
7375
cd examples;

MODULE.bazel

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ bazel_dep(name = "rules_license", version = "1.0.0")
4646
bazel_dep(name = "rules_pkg", version = "1.0.1")
4747
bazel_dep(name = "rules_python", version = "1.0.0")
4848

49+
# Pin to rules_proto to 7.1.0 to avoid toolchain incompatibilities when
50+
# --incompatible_enable_proto_toolchain_resolution=true in Bazel 7.
51+
# rules_proto 7.0.2 from deps incorrectly rules_proto's toolchain_type but protobuf's toolchain, but
52+
# 7.0.3+ uses protobuf's toolchain and toolchain_type but 7.0.3 isn't on BCR.
53+
# TODO: Restore as dev_dependency once Bazel 7 is dropped or pin is no longer needed.
54+
# rules_proto is needed for @com_google_protobuf_v25 used in //compatibility/... tests
55+
bazel_dep(name = "rules_proto", version = "7.1.0")
56+
4957
bazel_dep(name = "rules_rust", version = "0.56.0", dev_dependency = True)
5058
bazel_dep(name = "rules_ruby", version = "0.17.3", dev_dependency = True)
5159

@@ -82,6 +90,7 @@ ruby.toolchain(
8290
version = "system",
8391
)
8492
use_repo(ruby, "ruby")
93+
8594
ruby.bundle_fetch(
8695
name = "protobuf_bundle",
8796
gem_checksums = {
@@ -227,9 +236,6 @@ bazel_dep(
227236
cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
228237
use_repo(cc_configure, "local_config_cc")
229238

230-
# rules_proto are needed for @com_google_protobuf_v25 used in //compatibility/... tests
231-
bazel_dep(name = "rules_proto", version = "7.0.2", dev_dependency = True)
232-
233239
# For the Lua upb implementation
234240
bazel_dep(name = "lua", version = "5.4.6", dev_dependency = True)
235241

bazel/private/proto_info.bzl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@ def _from_root(root, repo, relpath):
3434
# - with sibling layout: `{root}/package/path`
3535
return _join(root, "" if repo.startswith("../") else repo, relpath)
3636

37-
def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace_root = "", bin_dir = None, allow_exports = None):
37+
def _create_proto_info(*, srcs, deps, descriptor_set, option_deps = [], proto_path = "", workspace_root = "", bin_dir = None, allow_exports = None):
3838
"""Constructs ProtoInfo.
3939
4040
Args:
4141
srcs: ([File]) List of .proto files (possibly under _virtual path)
4242
deps: ([ProtoInfo]) List of dependencies
43+
option_deps: ([ProtoInfo]) List of option dependencies
4344
descriptor_set: (File) Descriptor set for this Proto
4445
proto_path: (str) Path that should be stripped from files in srcs. When
4546
stripping is needed, the files should be symlinked into `_virtual_imports/target_name`
@@ -61,6 +62,8 @@ def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace
6162
fail("srcs parameter expects all files to have the same workspace_root: ", workspace_root)
6263
if not src.short_path.startswith(src_prefix):
6364
fail("srcs parameter expects all files start with %s" % src_prefix)
65+
if not srcs and option_deps:
66+
fail("option_deps parameter should not be set when srcs is empty")
6467
if type(descriptor_set) != "File":
6568
fail("descriptor_set parameter expected to be a File")
6669
if proto_path:
@@ -71,9 +74,10 @@ def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace
7174
if not bin_dir:
7275
fail("bin_dir parameter should be set when _virtual_imports are used")
7376

77+
protoc_deps = deps + option_deps
7478
transitive_sources = depset(
7579
direct = srcs,
76-
transitive = [dep.transitive_sources for dep in deps],
80+
transitive = [dep.transitive_sources for dep in protoc_deps],
7781
order = "preorder",
7882
)
7983

@@ -82,14 +86,15 @@ def _create_proto_info(*, srcs, deps, descriptor_set, proto_path = "", workspace
8286
root_paths = _uniq([src.root.path for src in srcs])
8387
transitive_proto_path = depset(
8488
direct = [_empty_to_dot(_from_root(root, workspace_root, proto_path)) for root in root_paths],
85-
transitive = [dep.transitive_proto_path for dep in deps],
89+
transitive = [dep.transitive_proto_path for dep in protoc_deps],
8690
)
8791

8892
if srcs:
8993
check_deps_sources = depset(direct = srcs)
9094
else:
9195
check_deps_sources = depset(transitive = [dep.check_deps_sources for dep in deps])
9296

97+
# Exclude option_deps from transitive descriptor sets.
9398
transitive_descriptor_sets = depset(
9499
direct = [descriptor_set],
95100
transitive = [dep.transitive_descriptor_sets for dep in deps],
@@ -122,11 +127,11 @@ ProtoInfo, _ = provider(
122127
fields = {
123128
"direct_sources": "(list[File]) The `.proto` source files from the `srcs` attribute.",
124129
"transitive_sources": """(depset[File]) The `.proto` source files from this rule and all
125-
its dependent protocol buffer rules.""",
130+
its `deps` and `option_deps` protocol buffer rules.""",
126131
"direct_descriptor_set": """(File) The descriptor set of the direct sources. If no srcs,
127132
contains an empty file.""",
128133
"transitive_descriptor_sets": """(depset[File]) A set of descriptor set files of all
129-
dependent `proto_library` rules, and this one's. This is not the same as passing
134+
its `deps` `proto_library` rules, and this one's. Excludes `option_deps`. This is not the same as passing
130135
--include_imports to proto-compiler. Will be empty if no dependencies.""",
131136
"proto_source_root": """(str) The directory relative to which the `.proto` files defined in
132137
the `proto_library` are defined. For example, if this is `a/b` and the rule has the
@@ -147,10 +152,10 @@ ProtoInfo, _ = provider(
147152
This will make it possible to fix `proto_library` in the future.
148153
""",
149154
"transitive_proto_path": """(depset(str) A set of `proto_source_root`s collected from the
150-
transitive closure of this rule.""",
155+
transitive closure of this rule's `deps` and `option_deps`.""",
151156
"check_deps_sources": """(depset[File]) The `.proto` sources from the 'srcs' attribute.
152157
If the library is a proxy library that has no sources, it contains the
153-
`check_deps_sources` from this library's direct deps.""",
158+
`check_deps_sources` from this library's direct `deps`.""",
154159
"allow_exports": """(Target) The packages where this proto_library can be exported.""",
155160

156161
# Deprecated fields:

bazel/private/proto_library_rule.bzl

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ load("//bazel/common:proto_common.bzl", "proto_common")
1515
load("//bazel/common:proto_info.bzl", "ProtoInfo")
1616
load("//bazel/private:toolchain_helpers.bzl", "toolchains")
1717

18-
STRICT_DEPS_FLAG_TEMPLATE = (
19-
#
18+
DIRECT_DEPS_FLAG_TEMPLATE = (
2019
"--direct_dependencies_violation_msg=" +
21-
"%%s is imported, but %s doesn't directly depend on a proto_library that 'srcs' it."
20+
"%%s is imported, but %s doesn't have direct `deps` on a proto_library that 'srcs' it."
21+
)
22+
23+
OPTION_DEPS_FLAG_TEMPLATE = (
24+
"--option_dependencies_violation_msg=" +
25+
"%%s is option imported, but %s doesn't have direct `option_deps` on a proto_library that 'srcs' it."
2226
)
2327

2428
def _check_srcs_package(target_package, srcs):
@@ -63,6 +67,7 @@ def _proto_library_impl(ctx):
6367
_check_srcs_package(ctx.label.package, ctx.attr.srcs)
6468
srcs = ctx.files.srcs
6569
deps = [dep[ProtoInfo] for dep in ctx.attr.deps]
70+
option_deps = [dep[ProtoInfo] for dep in ctx.attr.option_deps]
6671
exports = [dep[ProtoInfo] for dep in ctx.attr.exports]
6772
import_prefix = _get_import_prefix(ctx)
6873
strip_import_prefix = _get_strip_import_prefix(ctx)
@@ -77,17 +82,19 @@ def _proto_library_impl(ctx):
7782

7883
proto_path, virtual_srcs = _process_srcs(ctx, srcs, import_prefix, strip_import_prefix)
7984
descriptor_set = ctx.actions.declare_file(ctx.label.name + "-descriptor-set.proto.bin")
85+
8086
proto_info = ProtoInfo(
8187
srcs = virtual_srcs,
8288
deps = deps,
8389
descriptor_set = descriptor_set,
90+
option_deps = option_deps,
8491
proto_path = proto_path,
8592
workspace_root = ctx.label.workspace_root,
8693
bin_dir = ctx.bin_dir.path,
8794
allow_exports = ctx.attr.allow_exports,
8895
)
8996

90-
_write_descriptor_set(ctx, proto_info, deps, exports, descriptor_set)
97+
_write_descriptor_set(ctx, proto_info, deps, option_deps, exports, descriptor_set)
9198

9299
# We assume that the proto sources will not have conflicting artifacts
93100
# with the same root relative path
@@ -154,13 +161,13 @@ def _symlink_to_virtual_imports(ctx, srcs, import_prefix, strip_import_prefix):
154161
virtual_srcs.append(virtual_src)
155162
return proto_path, virtual_srcs
156163

157-
def _write_descriptor_set(ctx, proto_info, deps, exports, descriptor_set):
164+
def _write_descriptor_set(ctx, proto_info, deps, option_deps, exports, descriptor_set):
158165
"""Writes descriptor set."""
159166
if proto_info.direct_sources == []:
160167
ctx.actions.write(descriptor_set, "")
161168
return
162169

163-
dependencies_descriptor_sets = depset(transitive = [dep.transitive_descriptor_sets for dep in deps])
170+
dependencies_descriptor_sets = depset(transitive = [dep.transitive_descriptor_sets for dep in deps + option_deps])
164171

165172
args = ctx.actions.args()
166173

@@ -191,7 +198,22 @@ def _write_descriptor_set(ctx, proto_info, deps, exports, descriptor_set):
191198
args.add("--direct_dependencies=")
192199

193200
# Set `-direct_dependencies_violation_msg=`
194-
args.add(ctx.label, format = STRICT_DEPS_FLAG_TEMPLATE)
201+
args.add(ctx.label, format = DIRECT_DEPS_FLAG_TEMPLATE)
202+
203+
# option_deps can't be set unless sources is non-empty.
204+
option_importable_sources = depset(
205+
direct = proto_info.direct_sources,
206+
transitive = [dep.check_deps_sources for dep in option_deps],
207+
)
208+
args.add_joined(
209+
"--option_dependencies",
210+
option_importable_sources,
211+
map_each = proto_common.get_import_path,
212+
join_with = ":",
213+
)
214+
215+
# Set `-option_dependencies_violation_msg=`
216+
args.add(ctx.label, format = OPTION_DEPS_FLAG_TEMPLATE)
195217

196218
strict_imports = ctx.attr._strict_public_imports[BuildSettingInfo].value
197219
if strict_imports:
@@ -206,6 +228,7 @@ def _write_descriptor_set(ctx, proto_info, deps, exports, descriptor_set):
206228
map_each = proto_common.get_import_path,
207229
join_with = ":",
208230
)
231+
209232
if proto_common.INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION:
210233
toolchain = ctx.toolchains[toolchains.PROTO_TOOLCHAIN]
211234
if not toolchain:
@@ -283,6 +306,13 @@ This pattern can be used to e.g. export a public api under a persistent name."""
283306
doc = """
284307
The list of other <code>proto_library</code> rules that the target depends upon.
285308
A <code>proto_library</code> may only depend on other <code>proto_library</code>
309+
targets. It may not depend on language-specific libraries.""",
310+
),
311+
"option_deps": attr.label_list(
312+
providers = [ProtoInfo],
313+
doc = """
314+
The list of other <code>proto_library</code> rules that the target depends upon for options only.
315+
A <code>proto_library</code> may only depend on other <code>proto_library</code>
286316
targets. It may not depend on language-specific libraries.""",
287317
),
288318
"exports": attr.label_list(

bazel/proto_library.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ load("@proto_bazel_features//:features.bzl", "bazel_features")
1111
load("//bazel/private:proto_library_rule.bzl", _proto_library = "proto_library")
1212

1313
def proto_library(**kwattrs):
14-
# This condition causes Starlark rules to be used only on Bazel >=7.0.0
15-
if bazel_features.proto.starlark_proto_info:
14+
# Only use Starlark rules when they are removed from Bazel.
15+
if not hasattr(native, "proto_library"):
1616
_proto_library(**kwattrs)
1717
else:
1818
# On older Bazel versions keep using native rules, so that mismatch in ProtoInfo doesn't happen

protobuf_deps.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ def _github_archive(repo, commit, **kwargs):
5353
def protobuf_deps():
5454
"""Loads common dependencies needed to compile the protobuf library."""
5555

56+
# Pin rules_proto since Bazel 7 otherwise depends on rules_proto 5.3.0-21.7 which is missing
57+
# @rules_proto//proto:toolchain_type used by Bazel.
58+
# 6.0.0 should work too but would require users to add `register_toolchains` for rules_proto
59+
# to their WORKSPACE files.
60+
if not native.existing_rule("rules_proto"):
61+
http_archive(
62+
name = "rules_proto",
63+
strip_prefix = "rules_proto-7.1.0",
64+
url = "https://github.com/bazelbuild/rules_proto/releases/download/7.1.0/rules_proto-7.1.0.tar.gz",
65+
)
5666
if not native.existing_rule("bazel_features"):
5767
http_archive(
5868
name = "bazel_features",

0 commit comments

Comments
 (0)