Skip to content

Commit 2b99f07

Browse files
committed
Migrate proto_descriptor_set.
1 parent c47f34c commit 2b99f07

File tree

6 files changed

+304
-0
lines changed

6 files changed

+304
-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",

bazel/proto_descriptor_set.bzl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright 2020 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""A rule for generating a `FileDescriptorSet` with all transitive dependencies.
15+
16+
This module contains the definition of `proto_descriptor_set`, a rule that
17+
collects all `FileDescriptorSet`s from its transitive dependencies and generates
18+
a single `FileDescriptorSet` containing all the `FileDescriptorProto` from them.
19+
"""
20+
21+
load("//bazel/common:proto_info.bzl", "ProtoInfo")
22+
23+
def _proto_descriptor_set_impl(ctx):
24+
args = ctx.actions.args()
25+
26+
output = ctx.actions.declare_file("{}.pb".format(ctx.attr.name))
27+
args.add(output)
28+
29+
descriptor_sets = depset(
30+
transitive = [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.deps],
31+
)
32+
args.add_all(descriptor_sets)
33+
34+
ctx.actions.run(
35+
executable = ctx.executable._file_concat,
36+
mnemonic = "ConcatFileDescriptorSet",
37+
inputs = descriptor_sets,
38+
outputs = [output],
39+
arguments = [args],
40+
)
41+
42+
return [
43+
DefaultInfo(
44+
files = depset([output]),
45+
runfiles = ctx.runfiles(files = [output]),
46+
),
47+
]
48+
49+
proto_descriptor_set = rule(
50+
implementation = _proto_descriptor_set_impl,
51+
attrs = {
52+
"deps": attr.label_list(
53+
mandatory = False,
54+
providers = [ProtoInfo],
55+
doc = """
56+
Sequence of `ProtoInfo`s to collect `FileDescriptorSet`s from.
57+
""".strip(),
58+
),
59+
"_file_concat": attr.label(
60+
default = "//tools/file_concat",
61+
executable = True,
62+
cfg = "exec",
63+
),
64+
},
65+
doc = """
66+
Collects all `FileDescriptorSet`s from `deps` and combines them into a single
67+
`FileDescriptorSet` containing all the `FileDescriptorProto`.
68+
""".strip(),
69+
)

bazel/tests/BUILD

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ load(":proto_common_check_collocated_tests.bzl", "proto_common_check_collocated_
44
load(":proto_common_compile_tests.bzl", "proto_common_compile_test_suite")
55
load(":proto_common_declare_generated_files_tests.bzl", "proto_common_declare_generated_files_test_suite")
66
load(":proto_common_should_generate_tests.bzl", "proto_common_should_generate_test_suite")
7+
load("//bazel:proto_descriptor_set.bzl", "proto_descriptor_set")
8+
load("//bazel:proto_library.bzl", "proto_library")
79

810
package(default_applicable_licenses = ["//:license"])
911

@@ -18,3 +20,50 @@ 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+
25+
proto_library(
26+
name = "empty_proto_library",
27+
)
28+
29+
proto_descriptor_set(
30+
name = "no_protos",
31+
deps = [
32+
":empty_proto_library",
33+
],
34+
)
35+
36+
proto_descriptor_set(
37+
name = "well_known_protos",
38+
deps = [
39+
"//:any_proto",
40+
"//:api_proto",
41+
"//:compiler_plugin_proto",
42+
"//:descriptor_proto",
43+
"//:duration_proto",
44+
"//:empty_proto",
45+
"//:field_mask_proto",
46+
"//:source_context_proto",
47+
"//:struct_proto",
48+
"//:timestamp_proto",
49+
"//:type_proto",
50+
"//:wrappers_proto",
51+
],
52+
)
53+
54+
cc_test(
55+
name = "proto_descriptor_set_test",
56+
srcs = [
57+
"proto_descriptor_set_test.cc",
58+
],
59+
data = [
60+
":no_protos",
61+
":well_known_protos",
62+
],
63+
deps = [
64+
"@bazel_tools//tools/cpp/runfiles",
65+
"@googletest//:gtest",
66+
"@googletest//:gtest_main",
67+
"//:protobuf",
68+
],
69+
)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2020 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <algorithm>
16+
#include <fstream>
17+
#include <memory>
18+
#include <set>
19+
#include <string>
20+
#include <vector>
21+
22+
#include "tools/cpp/runfiles/runfiles.h"
23+
#include "google/protobuf/descriptor.pb.h"
24+
#include "gmock/gmock.h"
25+
#include "gtest/gtest.h"
26+
27+
28+
using bazel::tools::cpp::runfiles::Runfiles;
29+
using google::protobuf::FileDescriptorProto;
30+
using google::protobuf::FileDescriptorSet;
31+
32+
namespace rulesproto {
33+
constexpr char kWorkspaceRlocation[] = "protobuf/";
34+
constexpr char kWorkspaceRlocationBzlmod[] = "_main/";
35+
36+
namespace {
37+
38+
std::string GetRlocation(const std::string& file) {
39+
static std::unique_ptr<Runfiles> runfiles(Runfiles::CreateForTest());
40+
std::string path =
41+
runfiles->Rlocation(rulesproto::kWorkspaceRlocation + file);
42+
std::ifstream input(path, std::ifstream::binary);
43+
if (!input) {
44+
path = runfiles->Rlocation(rulesproto::kWorkspaceRlocationBzlmod + file);
45+
}
46+
return path;
47+
}
48+
49+
template <typename T, typename K>
50+
bool Contains(const T& container, const K& key) {
51+
return container.find(key) != container.end();
52+
}
53+
54+
std::vector<std::string> ReadFileDescriptorSet(const std::string& path) {
55+
std::ifstream input(path, std::ifstream::binary);
56+
EXPECT_TRUE(input) << "Could not open " << path;
57+
58+
FileDescriptorSet file_descriptor_set;
59+
EXPECT_TRUE(file_descriptor_set.ParseFromIstream(&input));
60+
61+
std::set<std::string> unordered_proto_files;
62+
for (FileDescriptorProto file_descriptor : file_descriptor_set.file()) {
63+
EXPECT_FALSE(Contains(unordered_proto_files, file_descriptor.name()))
64+
<< "Already saw " << file_descriptor.name();
65+
unordered_proto_files.insert(file_descriptor.name());
66+
}
67+
68+
std::vector<std::string> proto_files(unordered_proto_files.begin(),
69+
unordered_proto_files.end());
70+
std::sort(proto_files.begin(), proto_files.end());
71+
return proto_files;
72+
}
73+
74+
void AssertFileDescriptorSetContains(
75+
const std::string& path,
76+
const std::vector<std::string>& expected_proto_files) {
77+
std::vector<std::string> actual_proto_files =
78+
ReadFileDescriptorSet(GetRlocation(path));
79+
EXPECT_THAT(actual_proto_files,
80+
::testing::IsSupersetOf(expected_proto_files));
81+
}
82+
83+
} // namespace
84+
85+
TEST(ProtoDescriptorSetTest, NoProtos) {
86+
AssertFileDescriptorSetContains(
87+
"bazel/tests/no_protos.pb", {});
88+
}
89+
90+
TEST(ProtoDescriptorSetTest, WellKnownProtos) {
91+
AssertFileDescriptorSetContains(
92+
"bazel/tests/well_known_protos.pb",
93+
{
94+
"google/protobuf/any.proto",
95+
"google/protobuf/api.proto",
96+
"google/protobuf/descriptor.proto",
97+
"google/protobuf/duration.proto",
98+
"google/protobuf/empty.proto",
99+
"google/protobuf/field_mask.proto",
100+
"google/protobuf/source_context.proto",
101+
"google/protobuf/struct.proto",
102+
"google/protobuf/timestamp.proto",
103+
"google/protobuf/type.proto",
104+
"google/protobuf/wrappers.proto",
105+
});
106+
}
107+
108+
} // namespace rulesproto

tools/file_concat/BUILD.bazel

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 = ["//visibility:public"],
7+
)

tools/file_concat/main.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2020 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <fstream>
16+
#include <iostream>
17+
#include <string>
18+
19+
namespace {
20+
21+
constexpr size_t kBufferSize = 4096; // 4kB
22+
23+
// Return codes.
24+
constexpr int kOk = 0;
25+
constexpr int kUsageError = 1;
26+
constexpr int kIOError = 2;
27+
28+
} // namespace
29+
30+
int main(int argc, const char* argv[]) {
31+
if (argc < 2) {
32+
std::cout << "Usage: " << argv[0] << " <output> <inputs...>" << std::endl;
33+
return kUsageError;
34+
}
35+
36+
std::string output_path(argv[1]);
37+
std::ofstream output(output_path, std::ofstream::binary);
38+
if (!output) {
39+
std::cerr << "Could not open output file " << output_path << std::endl;
40+
return kIOError;
41+
}
42+
43+
for (int i = 2; i < argc; i++) {
44+
std::string input_path(argv[i]);
45+
std::ifstream input(input_path, std::ifstream::binary);
46+
if (!input) {
47+
std::cerr << "Could not open input file " << output_path << std::endl;
48+
return kIOError;
49+
}
50+
51+
char buffer[kBufferSize];
52+
while (input) {
53+
if (!input.read(buffer, kBufferSize) && !input.eof()) {
54+
std::cerr << "Error reading from " << input_path << std::endl;
55+
return kIOError;
56+
}
57+
if (!output.write(buffer, input.gcount())) {
58+
std::cerr << "Error writing to " << output_path << std::endl;
59+
return kIOError;
60+
}
61+
}
62+
}
63+
64+
return kOk;
65+
}

0 commit comments

Comments
 (0)