Skip to content

Commit 87af8c8

Browse files
jnthntatumcopybara-github
authored andcommitted
Add helper for building consistent descriptor pool.
PiperOrigin-RevId: 739215894
1 parent ccc9a70 commit 87af8c8

4 files changed

Lines changed: 410 additions & 0 deletions

File tree

tools/BUILD

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,32 @@ cc_test(
121121
"@com_google_protobuf//:protobuf",
122122
],
123123
)
124+
125+
cc_library(
126+
name = "descriptor_pool_builder",
127+
srcs = ["descriptor_pool_builder.cc"],
128+
hdrs = ["descriptor_pool_builder.h"],
129+
deps = [
130+
"//common:minimal_descriptor_database",
131+
"//internal:status_macros",
132+
"@com_google_absl//absl/base:nullability",
133+
"@com_google_absl//absl/container:flat_hash_set",
134+
"@com_google_absl//absl/status",
135+
"@com_google_absl//absl/strings",
136+
"@com_google_absl//absl/types:span",
137+
"@com_google_protobuf//:protobuf",
138+
],
139+
)
140+
141+
cc_test(
142+
name = "descriptor_pool_builder_test",
143+
srcs = ["descriptor_pool_builder_test.cc"],
144+
deps = [
145+
":descriptor_pool_builder",
146+
"//internal:testing",
147+
"@com_google_absl//absl/status",
148+
"@com_google_absl//absl/status:status_matchers",
149+
"@com_google_cel_spec//proto/cel/expr/conformance/proto2:test_all_types_cc_proto",
150+
"@com_google_protobuf//:protobuf",
151+
],
152+
)

tools/descriptor_pool_builder.cc

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2025 Google LLC
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+
// https://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 "tools/descriptor_pool_builder.h"
16+
17+
#include <memory>
18+
#include <vector>
19+
20+
#include "google/protobuf/descriptor.pb.h"
21+
#include "absl/base/nullability.h"
22+
#include "absl/container/flat_hash_set.h"
23+
#include "absl/status/status.h"
24+
#include "absl/strings/str_cat.h"
25+
#include "absl/types/span.h"
26+
#include "common/minimal_descriptor_database.h"
27+
#include "internal/status_macros.h"
28+
#include "google/protobuf/descriptor.h"
29+
30+
namespace cel {
31+
32+
namespace {
33+
34+
absl::Status FindDeps(
35+
std::vector<const google::protobuf::FileDescriptor*>& to_resolve,
36+
absl::flat_hash_set<const google::protobuf::FileDescriptor*>& resolved,
37+
DescriptorPoolBuilder& builder) {
38+
while (!to_resolve.empty()) {
39+
const auto* file = to_resolve.back();
40+
to_resolve.pop_back();
41+
if (resolved.contains(file)) {
42+
continue;
43+
}
44+
google::protobuf::FileDescriptorProto file_proto;
45+
file->CopyTo(&file_proto);
46+
// Note: order doesn't matter here as long as all the cross references are
47+
// correct in the final database.
48+
CEL_RETURN_IF_ERROR(builder.AddFileDescriptor(file_proto));
49+
resolved.insert(file);
50+
for (int i = 0; i < file->dependency_count(); ++i) {
51+
to_resolve.push_back(file->dependency(i));
52+
}
53+
}
54+
return absl::OkStatus();
55+
}
56+
57+
} // namespace
58+
59+
DescriptorPoolBuilder::StateHolder::StateHolder(
60+
google::protobuf::DescriptorDatabase* base)
61+
: base(base), merged(base, &extensions), pool(&merged) {}
62+
63+
DescriptorPoolBuilder::DescriptorPoolBuilder()
64+
: state_(std::make_shared<DescriptorPoolBuilder::StateHolder>(
65+
cel::GetMinimalDescriptorDatabase())) {}
66+
67+
std::shared_ptr<const google::protobuf::DescriptorPool>
68+
DescriptorPoolBuilder::Build() && {
69+
auto alias =
70+
std::shared_ptr<const google::protobuf::DescriptorPool>(state_, &state_->pool);
71+
state_.reset();
72+
return alias;
73+
}
74+
75+
absl::Status DescriptorPoolBuilder::AddTransitiveDescriptorSet(
76+
absl::Nonnull<const google::protobuf::Descriptor*> desc) {
77+
absl::flat_hash_set<const google::protobuf::FileDescriptor*> resolved;
78+
std::vector<const google::protobuf::FileDescriptor*> to_resolve{desc->file()};
79+
return FindDeps(to_resolve, resolved, *this);
80+
}
81+
82+
absl::Status DescriptorPoolBuilder::AddTransitiveDescriptorSet(
83+
absl::Span<absl::Nonnull<const google::protobuf::Descriptor*>> descs) {
84+
absl::flat_hash_set<const google::protobuf::FileDescriptor*> resolved;
85+
std::vector<absl::Nonnull<const google::protobuf::FileDescriptor*>> to_resolve;
86+
to_resolve.reserve(descs.size());
87+
for (const google::protobuf::Descriptor* desc : descs) {
88+
to_resolve.push_back(desc->file());
89+
}
90+
91+
return FindDeps(to_resolve, resolved, *this);
92+
}
93+
94+
absl::Status DescriptorPoolBuilder::AddFileDescriptor(
95+
const google::protobuf::FileDescriptorProto& file) {
96+
if (!state_->extensions.Add(file)) {
97+
return absl::InvalidArgumentError(
98+
absl::StrCat("proto descriptor conflict: ", file.name()));
99+
}
100+
return absl::OkStatus();
101+
}
102+
103+
absl::Status DescriptorPoolBuilder::AddFileDescriptorSet(
104+
const google::protobuf::FileDescriptorSet& file) {
105+
for (const auto& file : file.file()) {
106+
CEL_RETURN_IF_ERROR(AddFileDescriptor(file));
107+
}
108+
return absl::OkStatus();
109+
}
110+
111+
} // namespace cel

tools/descriptor_pool_builder.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2025 Google LLC
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+
// https://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+
#ifndef THIRD_PARTY_CEL_CPP_TOOLS_DESCRIPTOR_POOL_BUILDER_H_
16+
#define THIRD_PARTY_CEL_CPP_TOOLS_DESCRIPTOR_POOL_BUILDER_H_
17+
18+
#include <memory>
19+
#include <utility>
20+
21+
#include "google/protobuf/descriptor.pb.h"
22+
#include "absl/base/nullability.h"
23+
#include "absl/status/status.h"
24+
#include "absl/types/span.h"
25+
#include "google/protobuf/descriptor.h"
26+
#include "google/protobuf/descriptor_database.h"
27+
28+
namespace cel {
29+
30+
// A helper class for building a descriptor pool from a set proto file
31+
// descriptors. Manages lifetime for the descriptor databases backing
32+
// the pool.
33+
//
34+
// Client must ensure that types are not added multiple times.
35+
//
36+
// Note: in the constructed pool, the definitions for the required types for
37+
// CEL will shadow any added to the builder. Clients should not modify types
38+
// from the google.protobuf package in general, but if they do the behavior of
39+
// the constructed descriptor pool will be inconsistent.
40+
class DescriptorPoolBuilder {
41+
public:
42+
DescriptorPoolBuilder();
43+
44+
DescriptorPoolBuilder& operator=(const DescriptorPoolBuilder&) = delete;
45+
DescriptorPoolBuilder(const DescriptorPoolBuilder&) = delete;
46+
DescriptorPoolBuilder& operator=(const DescriptorPoolBuilder&&) = delete;
47+
DescriptorPoolBuilder(DescriptorPoolBuilder&&) = delete;
48+
49+
~DescriptorPoolBuilder() = default;
50+
51+
// Returns a shared pointer to the new descriptor pool that manages the
52+
// underlying descriptor databases backing the pool.
53+
//
54+
// Consumes the builder instance. It is unsafe to make any further changes
55+
// to the descriptor databases after accessing the pool.
56+
std::shared_ptr<const google::protobuf::DescriptorPool> Build() &&;
57+
58+
// Utility for adding the transitive dependencies of a message with a linked
59+
// descriptor.
60+
absl::Status AddTransitiveDescriptorSet(
61+
absl::Nonnull<const google::protobuf::Descriptor*> desc);
62+
63+
absl::Status AddTransitiveDescriptorSet(
64+
absl::Span<absl::Nonnull<const google::protobuf::Descriptor*>>);
65+
66+
// Adds a file descriptor set to the pool. Client must ensure that all
67+
// dependencies are satisfied and that files are not added multiple times.
68+
absl::Status AddFileDescriptorSet(const google::protobuf::FileDescriptorSet& files);
69+
70+
// Adds a single proto file descriptor set to the pool. Client must ensure
71+
// that all dependencies are satisfied and that files are not added multiple
72+
// times.
73+
absl::Status AddFileDescriptor(const google::protobuf::FileDescriptorProto& file);
74+
75+
private:
76+
struct StateHolder {
77+
explicit StateHolder(google::protobuf::DescriptorDatabase* base);
78+
79+
google::protobuf::DescriptorDatabase* base;
80+
google::protobuf::SimpleDescriptorDatabase extensions;
81+
google::protobuf::MergedDescriptorDatabase merged;
82+
google::protobuf::DescriptorPool pool;
83+
};
84+
85+
explicit DescriptorPoolBuilder(std::shared_ptr<StateHolder> state)
86+
: state_(std::move(state)) {}
87+
88+
std::shared_ptr<StateHolder> state_;
89+
};
90+
91+
} // namespace cel
92+
93+
#endif // THIRD_PARTY_CEL_CPP_TOOLS_DESCRIPTOR_POOL_BUILDER_H_

0 commit comments

Comments
 (0)