Skip to content

Commit b98f6ee

Browse files
Unify plugin and built-in generators to use request/response interface
PiperOrigin-RevId: 801065870
1 parent aad0daa commit b98f6ee

File tree

3 files changed

+127
-90
lines changed

3 files changed

+127
-90
lines changed

src/google/protobuf/compiler/command_line_interface.cc

Lines changed: 101 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <string>
2929
#include <utility>
3030
#include <vector>
31+
32+
#include "google/protobuf/compiler/plugin.h"
3133
#ifdef major
3234
#undef major
3335
#endif
@@ -320,7 +322,7 @@ void CommandLineInterface::GetTransitiveDependencies(
320322
const FileDescriptor* file,
321323
absl::flat_hash_set<const FileDescriptor*>* already_seen,
322324
RepeatedPtrField<FileDescriptorProto>* output,
323-
const TransitiveDependencyOptions& options) {
325+
const TransitiveDependencyOptions& options) const {
324326
if (!already_seen->insert(file).second) {
325327
// Already saw this file. Skip.
326328
return;
@@ -2733,30 +2735,8 @@ bool CommandLineInterface::GenerateOutput(
27332735
}
27342736
} else {
27352737
// Regular generator.
2736-
std::string parameters = output_directive.parameter;
2737-
if (!generator_parameters_[output_directive.name].empty()) {
2738-
if (!parameters.empty()) {
2739-
parameters.append(",");
2740-
}
2741-
parameters.append(generator_parameters_[output_directive.name]);
2742-
}
2743-
if (!EnforceProto3OptionalSupport(
2744-
output_directive.name,
2745-
output_directive.generator->GetSupportedFeatures(), parsed_files)) {
2746-
return false;
2747-
}
2748-
2749-
if (!EnforceEditionsSupport(
2750-
output_directive.name,
2751-
output_directive.generator->GetSupportedFeatures(),
2752-
output_directive.generator->GetMinimumEdition(),
2753-
output_directive.generator->GetMaximumEdition(), parsed_files)) {
2754-
return false;
2755-
}
2756-
2757-
if (!output_directive.generator->GenerateAll(parsed_files, parameters,
2758-
generator_context, &error)) {
2759-
// Generator returned an error.
2738+
if (!GenerateBuiltInOutput(parsed_files, output_directive,
2739+
generator_context, &error)) {
27602740
std::cerr << output_directive.name << ": " << error << std::endl;
27612741
return false;
27622742
}
@@ -2846,22 +2826,16 @@ bool CommandLineInterface::GenerateDependencyManifestFile(
28462826
return true;
28472827
}
28482828

2849-
bool CommandLineInterface::GeneratePluginOutput(
2850-
const std::vector<const FileDescriptor*>& parsed_files,
2851-
const std::string& plugin_name, const std::string& parameter,
2852-
GeneratorContext* generator_context, std::string* error) {
2829+
CodeGeneratorRequest CommandLineInterface::CreateCodeGeneratorRequest(
2830+
std::vector<const FileDescriptor*> parsed_files, std::string parameter,
2831+
bool copy_json_name, bool bootstrap) const {
28532832
CodeGeneratorRequest request;
2854-
CodeGeneratorResponse response;
2855-
std::string processed_parameter = parameter;
2856-
2857-
bool bootstrap = GetBootstrapParam(processed_parameter);
28582833

28592834
// Build the request.
2860-
if (!processed_parameter.empty()) {
2861-
request.set_parameter(processed_parameter);
2835+
if (!parameter.empty()) {
2836+
request.set_parameter(parameter);
28622837
}
28632838

2864-
28652839
absl::flat_hash_set<const FileDescriptor*> already_seen;
28662840
for (const FileDescriptor* file : parsed_files) {
28672841
request.add_file_to_generate(file->name());
@@ -2877,9 +2851,6 @@ bool CommandLineInterface::GeneratePluginOutput(
28772851
const DescriptorPool* pool = parsed_files[0]->pool();
28782852
absl::flat_hash_set<std::string> files_to_generate(input_files_.begin(),
28792853
input_files_.end());
2880-
static const auto builtin_plugins = new absl::flat_hash_set<std::string>(
2881-
{"protoc-gen-cpp", "protoc-gen-java", "protoc-gen-mutable_java",
2882-
"protoc-gen-python"});
28832854
for (FileDescriptorProto& file_proto : *request.mutable_proto_file()) {
28842855
if (files_to_generate.contains(file_proto.name())) {
28852856
const FileDescriptor* file = pool->FindFileByName(file_proto.name());
@@ -2889,8 +2860,7 @@ bool CommandLineInterface::GeneratePluginOutput(
28892860
if (!bootstrap) {
28902861
file->CopySourceCodeInfoTo(&file_proto);
28912862

2892-
// The built-in code generators didn't use the json names.
2893-
if (!builtin_plugins->contains(plugin_name)) {
2863+
if (copy_json_name) {
28942864
file->CopyJsonNameTo(&file_proto);
28952865
}
28962866
}
@@ -2905,21 +2875,12 @@ bool CommandLineInterface::GeneratePluginOutput(
29052875
version->set_patch(PROTOBUF_VERSION % 1000);
29062876
version->set_suffix(PROTOBUF_VERSION_SUFFIX);
29072877

2908-
// Invoke the plugin.
2909-
Subprocess subprocess;
2910-
2911-
if (plugins_.count(plugin_name) > 0) {
2912-
subprocess.Start(plugins_[plugin_name], Subprocess::EXACT_NAME);
2913-
} else {
2914-
subprocess.Start(plugin_name, Subprocess::SEARCH_PATH);
2915-
}
2916-
2917-
std::string communicate_error;
2918-
if (!subprocess.Communicate(request, &response, &communicate_error)) {
2919-
*error = absl::Substitute("$0: $1", plugin_name, communicate_error);
2920-
return false;
2921-
}
2878+
return request;
2879+
}
29222880

2881+
bool CommandLineInterface::GenerateCodeFromResponse(
2882+
const CodeGeneratorResponse& response, GeneratorContext* generator_context,
2883+
bool bootstrap, std::string plugin_name, std::string* error) {
29232884
// Write the files. We do this even if there was a generator error in order
29242885
// to match the behavior of a compiled-in generator.
29252886
std::unique_ptr<io::ZeroCopyOutputStream> current_output;
@@ -2956,6 +2917,46 @@ bool CommandLineInterface::GeneratePluginOutput(
29562917
writer.WriteString(output_file.content());
29572918
}
29582919

2920+
return true;
2921+
}
2922+
2923+
bool CommandLineInterface::GeneratePluginOutput(
2924+
const std::vector<const FileDescriptor*>& parsed_files,
2925+
const std::string& plugin_name, const std::string& parameter,
2926+
GeneratorContext* generator_context, std::string* error) {
2927+
// TODO Remove these special-cases and send json names to all
2928+
// plugins.
2929+
static const auto builtin_plugins = new absl::flat_hash_set<std::string>(
2930+
{"protoc-gen-cpp", "protoc-gen-java", "protoc-gen-mutable_java",
2931+
"protoc-gen-python"});
2932+
2933+
bool bootstrap = GetBootstrapParam(parameter);
2934+
CodeGeneratorRequest request = CreateCodeGeneratorRequest(
2935+
parsed_files, parameter,
2936+
// The built-in code generators didn't use the json names.
2937+
/*copy_json_name=*/!builtin_plugins->contains(plugin_name), bootstrap);
2938+
CodeGeneratorResponse response;
2939+
2940+
// Invoke the plugin.
2941+
Subprocess subprocess;
2942+
2943+
if (plugins_.count(plugin_name) > 0) {
2944+
subprocess.Start(plugins_[plugin_name], Subprocess::EXACT_NAME);
2945+
} else {
2946+
subprocess.Start(plugin_name, Subprocess::SEARCH_PATH);
2947+
}
2948+
2949+
std::string communicate_error;
2950+
if (!subprocess.Communicate(request, &response, &communicate_error)) {
2951+
*error = absl::Substitute("$0: $1", plugin_name, communicate_error);
2952+
return false;
2953+
}
2954+
2955+
if (!GenerateCodeFromResponse(response, generator_context, bootstrap,
2956+
plugin_name, error)) {
2957+
return false;
2958+
}
2959+
29592960
// Check for errors.
29602961
bool success = true;
29612962
if (!EnforceProto3OptionalSupport(plugin_name, response.supported_features(),
@@ -2977,6 +2978,51 @@ bool CommandLineInterface::GeneratePluginOutput(
29772978
return success;
29782979
}
29792980

2981+
bool CommandLineInterface::GenerateBuiltInOutput(
2982+
const std::vector<const FileDescriptor*>& parsed_files,
2983+
const OutputDirective& output_directive,
2984+
GeneratorContext* generator_context, std::string* error) {
2985+
std::string parameters = output_directive.parameter;
2986+
if (!generator_parameters_[output_directive.name].empty()) {
2987+
if (!parameters.empty()) {
2988+
parameters.append(",");
2989+
}
2990+
parameters.append(generator_parameters_[output_directive.name]);
2991+
}
2992+
if (!EnforceProto3OptionalSupport(
2993+
output_directive.name,
2994+
output_directive.generator->GetSupportedFeatures(), parsed_files)) {
2995+
return false;
2996+
}
2997+
2998+
if (!EnforceEditionsSupport(
2999+
output_directive.name,
3000+
output_directive.generator->GetSupportedFeatures(),
3001+
output_directive.generator->GetMinimumEdition(),
3002+
output_directive.generator->GetMaximumEdition(), parsed_files)) {
3003+
return false;
3004+
}
3005+
3006+
CodeGeneratorRequest request =
3007+
CreateCodeGeneratorRequest(parsed_files, parameters);
3008+
CodeGeneratorResponse response;
3009+
if (!GenerateCode(request, *output_directive.generator, &response, error)) {
3010+
return false;
3011+
}
3012+
if (response.has_error()) {
3013+
*error = response.error();
3014+
return false;
3015+
}
3016+
3017+
if (!GenerateCodeFromResponse(response, generator_context,
3018+
/*bootstrap=*/false, output_directive.name,
3019+
error)) {
3020+
return false;
3021+
}
3022+
3023+
return true;
3024+
}
3025+
29803026
bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) {
29813027
// Look up the type.
29823028
const Descriptor* type = pool->FindMessageTypeByName(codec_type_);

src/google/protobuf/compiler/command_line_interface.h

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "absl/container/flat_hash_map.h"
2727
#include "absl/container/flat_hash_set.h"
2828
#include "absl/strings/string_view.h"
29+
#include "google/protobuf/compiler/plugin.pb.h"
2930
#include "google/protobuf/descriptor.pb.h"
3031
#include "google/protobuf/descriptor_database.h"
3132
#include "google/protobuf/port.h"
@@ -222,17 +223,6 @@ class PROTOC_EXPORT CommandLineInterface {
222223
bool MakeInputsBeProtoPathRelative(DiskSourceTree* source_tree,
223224
DescriptorDatabase* fallback_database);
224225

225-
// Fails if these files use proto3 optional and the code generator doesn't
226-
// support it. This is a permanent check.
227-
bool EnforceProto3OptionalSupport(
228-
const std::string& codegen_name, uint64_t supported_features,
229-
const std::vector<const FileDescriptor*>& parsed_files) const;
230-
231-
bool EnforceEditionsSupport(
232-
const std::string& codegen_name, uint64_t supported_features,
233-
Edition minimum_edition, Edition maximum_edition,
234-
const std::vector<const FileDescriptor*>& parsed_files) const;
235-
236226
bool EnforceProtocEditionsSupport(
237227
const std::vector<const FileDescriptor*>& parsed_files) const;
238228

@@ -295,6 +285,30 @@ class PROTOC_EXPORT CommandLineInterface {
295285
const std::vector<const FileDescriptor*>& parsed_files,
296286
const std::string& plugin_name, const std::string& parameter,
297287
GeneratorContext* generator_context, std::string* error);
288+
bool GenerateBuiltInOutput(
289+
const std::vector<const FileDescriptor*>& parsed_files,
290+
const OutputDirective& output_directive,
291+
GeneratorContext* generator_context, std::string* error);
292+
293+
// Common code for both plugins and built-in generators.
294+
CodeGeneratorRequest CreateCodeGeneratorRequest(
295+
std::vector<const FileDescriptor*> parsed_files, std::string parameter,
296+
bool copy_json_name = false, bool bootstrap = false) const;
297+
bool GenerateCodeFromResponse(const CodeGeneratorResponse& response,
298+
GeneratorContext* generator_context,
299+
bool bootstrap, std::string plugin_name,
300+
std::string* error);
301+
302+
// Fails if these files use proto3 optional and the code generator doesn't
303+
// support it. This is a permanent check.
304+
bool EnforceProto3OptionalSupport(
305+
const std::string& codegen_name, uint64_t supported_features,
306+
const std::vector<const FileDescriptor*>& parsed_files) const;
307+
308+
bool EnforceEditionsSupport(
309+
const std::string& codegen_name, uint64_t supported_features,
310+
Edition minimum_edition, Edition maximum_edition,
311+
const std::vector<const FileDescriptor*>& parsed_files) const;
298312

299313
// Implements --encode and --decode.
300314
bool EncodeOrDecode(const DescriptorPool* pool);
@@ -342,7 +356,7 @@ class PROTOC_EXPORT CommandLineInterface {
342356
absl::flat_hash_set<const FileDescriptor*>* already_seen,
343357
RepeatedPtrField<FileDescriptorProto>* output,
344358
const TransitiveDependencyOptions& options =
345-
TransitiveDependencyOptions());
359+
TransitiveDependencyOptions()) const;
346360

347361

348362
// -----------------------------------------------------------------

src/google/protobuf/compiler/java/file.cc

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,6 @@ bool CollectExtensions(const Message& message, FieldDescriptorSet* extensions) {
105105
return true;
106106
}
107107

108-
void CollectPublicDependencies(
109-
const FileDescriptor* file,
110-
absl::flat_hash_set<const FileDescriptor*>* dependencies) {
111-
if (file == nullptr || !dependencies->insert(file).second) return;
112-
for (int i = 0; file != nullptr && i < file->public_dependency_count(); i++) {
113-
CollectPublicDependencies(file->public_dependency(i), dependencies);
114-
}
115-
}
116-
117108
// Finds all extensions for custom options in the given file descriptor with the
118109
// builder pool which resolves Java features instead of the generated pool.
119110
void CollectExtensions(const FileDescriptor& file,
@@ -138,20 +129,6 @@ void CollectExtensions(const FileDescriptor& file,
138129
extensions->clear();
139130
// Unknown extensions are ok and expected in the case of option imports.
140131
CollectExtensions(*dynamic_file_proto, extensions);
141-
142-
// TODO: Remove descriptor pool pollution from protoc full.
143-
// Check against dependencies to handle option dependencies polluting pool
144-
// from using protoc_full with built-in generators instead of plugins.
145-
// Option dependencies and transitive dependencies are not allowed, except in
146-
// the case of import public.
147-
absl::flat_hash_set<const FileDescriptor*> dependencies;
148-
dependencies.insert(&file);
149-
for (int i = 0; i < file.dependency_count(); i++) {
150-
CollectPublicDependencies(file.dependency(i), &dependencies);
151-
}
152-
absl::erase_if(*extensions, [&](const FieldDescriptor* fieldDescriptor) {
153-
return !dependencies.contains(fieldDescriptor->file());
154-
});
155132
}
156133

157134
// Our static initialization methods can become very, very large.

0 commit comments

Comments
 (0)