Skip to content

Commit 8d4dfdd

Browse files
SpencerCcopybara-github
authored andcommitted
Migrate proto_descriptor_set (#23369)
This is just a straightforward copy of https://github.com/bazelbuild/rules_proto/blob/main/proto/private/rules/proto_descriptor_set.bzl with the related tooling and test files. The only modifications are: - Changed paths to match the organization of this repo - Included constants directly in the test file instead of a separate header in a separate directory. Closes #19647 Closes #23369 COPYBARA_INTEGRATE_REVIEW=#23369 from SpencerC:proto_descriptor_set 24b517b PiperOrigin-RevId: 814276774
1 parent dead64c commit 8d4dfdd

File tree

6 files changed

+279
-0
lines changed

6 files changed

+279
-0
lines changed

bazel/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ bzl_library(
6464
deps = ["//bazel/private:upb_proto_library_internal_bzl"],
6565
)
6666

67+
bzl_library(
68+
name = "proto_descriptor_set_bzl",
69+
srcs = ["proto_descriptor_set.bzl"],
70+
visibility = ["//visibility:public"],
71+
)
72+
6773
# The data in this target is exposed in //bazel/private:for_bazel_tests
6874
filegroup(
6975
name = "for_bazel_tests",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cc_binary(
2+
name = "file_concat",
3+
srcs = [
4+
"main.cc",
5+
],
6+
visibility = ["//bazel:__subpackages__"],
7+
)

bazel/private/file_concat/main.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) 2020-2025, Google LLC
2+
// 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+
#include <fstream>
9+
#include <iostream>
10+
#include <string>
11+
12+
namespace {
13+
14+
constexpr size_t kBufferSize = 4096; // 4kB
15+
16+
// Return codes.
17+
constexpr int kOk = 0;
18+
constexpr int kUsageError = 1;
19+
constexpr int kIOError = 2;
20+
21+
} // namespace
22+
23+
int main(int argc, const char* argv[]) {
24+
if (argc < 2) {
25+
std::cout << "Usage: " << argv[0] << " <output> <inputs...>" << std::endl;
26+
return kUsageError;
27+
}
28+
29+
std::string output_path(argv[1]);
30+
std::ofstream output(output_path, std::ofstream::binary);
31+
if (!output) {
32+
std::cerr << "Could not open output file " << output_path << std::endl;
33+
return kIOError;
34+
}
35+
36+
for (int i = 2; i < argc; i++) {
37+
std::string input_path(argv[i]);
38+
std::ifstream input(input_path, std::ifstream::binary);
39+
if (!input) {
40+
std::cerr << "Could not open input file " << output_path << std::endl;
41+
return kIOError;
42+
}
43+
44+
char buffer[kBufferSize];
45+
while (input) {
46+
if (!input.read(buffer, kBufferSize) && !input.eof()) {
47+
std::cerr << "Error reading from " << input_path << std::endl;
48+
return kIOError;
49+
}
50+
if (!output.write(buffer, input.gcount())) {
51+
std::cerr << "Error writing to " << output_path << std::endl;
52+
return kIOError;
53+
}
54+
}
55+
}
56+
57+
return kOk;
58+
}

bazel/proto_descriptor_set.bzl

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) 2020-2025, Google LLC
2+
# 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+
"""A rule for generating a `FileDescriptorSet` with all transitive dependencies.
8+
9+
This module contains the definition of `proto_descriptor_set`, a rule that
10+
collects all `FileDescriptorSet`s from its transitive dependencies and generates
11+
a single `FileDescriptorSet` containing all the `FileDescriptorProto` from them.
12+
"""
13+
14+
load("//bazel/common:proto_info.bzl", "ProtoInfo")
15+
16+
def _proto_descriptor_set_impl(ctx):
17+
args = ctx.actions.args()
18+
19+
output = ctx.actions.declare_file("{}.pb".format(ctx.attr.name))
20+
args.add(output)
21+
22+
descriptor_sets = depset(
23+
transitive = [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.deps],
24+
)
25+
args.add_all(descriptor_sets)
26+
27+
ctx.actions.run(
28+
executable = ctx.executable._file_concat,
29+
mnemonic = "ConcatFileDescriptorSet",
30+
inputs = descriptor_sets,
31+
outputs = [output],
32+
arguments = [args],
33+
)
34+
35+
return [
36+
DefaultInfo(
37+
files = depset([output]),
38+
runfiles = ctx.runfiles(files = [output]),
39+
),
40+
]
41+
42+
proto_descriptor_set = rule(
43+
implementation = _proto_descriptor_set_impl,
44+
attrs = {
45+
"deps": attr.label_list(
46+
mandatory = False,
47+
providers = [ProtoInfo],
48+
doc = """
49+
Sequence of `ProtoInfo`s to collect `FileDescriptorSet`s from.
50+
""".strip(),
51+
),
52+
"_file_concat": attr.label(
53+
default = "//bazel/private/file_concat:file_concat",
54+
executable = True,
55+
cfg = "exec",
56+
),
57+
},
58+
doc = """
59+
Collects all `FileDescriptorSet`s from `deps` and combines them into a single
60+
`FileDescriptorSet` containing all the `FileDescriptorProto`.
61+
""".strip(),
62+
)

bazel/tests/BUILD

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
load("//bazel:proto_descriptor_set.bzl", "proto_descriptor_set")
2+
load("//bazel:proto_library.bzl", "proto_library")
13
load(":bazel_proto_library_tests.bzl", "bazel_proto_library_test_suite")
24
load(":java_proto_library_tests.bzl", "java_proto_library_test_suite")
35
load(":proto_common_check_collocated_tests.bzl", "proto_common_check_collocated_test_suite")
@@ -18,3 +20,49 @@ proto_common_check_collocated_test_suite(name = "proto_common_check_collocated_t
1820
bazel_proto_library_test_suite(name = "bazel_proto_library_test_suite")
1921

2022
java_proto_library_test_suite(name = "java_proto_library_test_suite")
23+
24+
proto_library(
25+
name = "empty_proto_library",
26+
)
27+
28+
proto_descriptor_set(
29+
name = "no_protos",
30+
deps = [
31+
":empty_proto_library",
32+
],
33+
)
34+
35+
proto_descriptor_set(
36+
name = "well_known_protos",
37+
deps = [
38+
"//:any_proto",
39+
"//:api_proto",
40+
"//:compiler_plugin_proto",
41+
"//:descriptor_proto",
42+
"//:duration_proto",
43+
"//:empty_proto",
44+
"//:field_mask_proto",
45+
"//:source_context_proto",
46+
"//:struct_proto",
47+
"//:timestamp_proto",
48+
"//:type_proto",
49+
"//:wrappers_proto",
50+
],
51+
)
52+
53+
cc_test(
54+
name = "proto_descriptor_set_test",
55+
srcs = [
56+
"proto_descriptor_set_test.cc",
57+
],
58+
data = [
59+
":no_protos",
60+
":well_known_protos",
61+
],
62+
deps = [
63+
"//:protobuf",
64+
"@bazel_tools//tools/cpp/runfiles",
65+
"@googletest//:gtest",
66+
"@googletest//:gtest_main",
67+
],
68+
)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) 2020-2025, Google LLC
2+
// 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+
#include <algorithm>
9+
#include <fstream>
10+
#include <memory>
11+
#include <set>
12+
#include <string>
13+
#include <vector>
14+
15+
#include "gmock/gmock.h"
16+
#include "google/protobuf/descriptor.pb.h"
17+
#include "gtest/gtest.h"
18+
#include "tools/cpp/runfiles/runfiles.h"
19+
20+
using bazel::tools::cpp::runfiles::Runfiles;
21+
using google::protobuf::FileDescriptorProto;
22+
using google::protobuf::FileDescriptorSet;
23+
24+
namespace rulesproto {
25+
constexpr char kWorkspaceRlocation[] = "protobuf/";
26+
constexpr char kWorkspaceRlocationBzlmod[] = "_main/";
27+
28+
namespace {
29+
30+
std::string GetRlocation(const std::string& file) {
31+
static std::unique_ptr<Runfiles> runfiles(Runfiles::CreateForTest());
32+
std::string path =
33+
runfiles->Rlocation(rulesproto::kWorkspaceRlocation + file);
34+
std::ifstream input(path, std::ifstream::binary);
35+
if (!input) {
36+
path = runfiles->Rlocation(rulesproto::kWorkspaceRlocationBzlmod + file);
37+
}
38+
return path;
39+
}
40+
41+
template <typename T, typename K>
42+
bool Contains(const T& container, const K& key) {
43+
return container.find(key) != container.end();
44+
}
45+
46+
std::vector<std::string> ReadFileDescriptorSet(const std::string& path) {
47+
std::ifstream input(path, std::ifstream::binary);
48+
EXPECT_TRUE(input) << "Could not open " << path;
49+
50+
FileDescriptorSet file_descriptor_set;
51+
EXPECT_TRUE(file_descriptor_set.ParseFromIstream(&input));
52+
53+
std::set<std::string> unordered_proto_files;
54+
for (FileDescriptorProto file_descriptor : file_descriptor_set.file()) {
55+
EXPECT_FALSE(Contains(unordered_proto_files, file_descriptor.name()))
56+
<< "Already saw " << file_descriptor.name();
57+
unordered_proto_files.insert(file_descriptor.name());
58+
}
59+
60+
std::vector<std::string> proto_files(unordered_proto_files.begin(),
61+
unordered_proto_files.end());
62+
std::sort(proto_files.begin(), proto_files.end());
63+
return proto_files;
64+
}
65+
66+
void AssertFileDescriptorSetContains(
67+
const std::string& path,
68+
const std::vector<std::string>& expected_proto_files) {
69+
std::vector<std::string> actual_proto_files =
70+
ReadFileDescriptorSet(GetRlocation(path));
71+
EXPECT_THAT(actual_proto_files,
72+
::testing::IsSupersetOf(expected_proto_files));
73+
}
74+
75+
} // namespace
76+
77+
TEST(ProtoDescriptorSetTest, NoProtos) {
78+
AssertFileDescriptorSetContains("bazel/tests/no_protos.pb", {});
79+
}
80+
81+
TEST(ProtoDescriptorSetTest, WellKnownProtos) {
82+
AssertFileDescriptorSetContains("bazel/tests/well_known_protos.pb",
83+
{
84+
"google/protobuf/any.proto",
85+
"google/protobuf/api.proto",
86+
"google/protobuf/descriptor.proto",
87+
"google/protobuf/duration.proto",
88+
"google/protobuf/empty.proto",
89+
"google/protobuf/field_mask.proto",
90+
"google/protobuf/source_context.proto",
91+
"google/protobuf/struct.proto",
92+
"google/protobuf/timestamp.proto",
93+
"google/protobuf/type.proto",
94+
"google/protobuf/wrappers.proto",
95+
});
96+
}
97+
98+
} // namespace rulesproto

0 commit comments

Comments
 (0)