1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/protozero/protoc_plugin/protozero_generator.h"
18
19 #include <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23
24 #include "google/protobuf/descriptor.h"
25 #include "google/protobuf/io/printer.h"
26 #include "google/protobuf/io/zero_copy_stream.h"
27 #include "google/protobuf/stubs/strutil.h"
28
29 namespace protozero {
30
31 using google::protobuf::Descriptor; // Message descriptor.
32 using google::protobuf::EnumDescriptor;
33 using google::protobuf::EnumValueDescriptor;
34 using google::protobuf::FieldDescriptor;
35 using google::protobuf::FileDescriptor;
36 using google::protobuf::compiler::GeneratorContext;
37 using google::protobuf::io::Printer;
38 using google::protobuf::io::ZeroCopyOutputStream;
39
40 using google::protobuf::Split;
41 using google::protobuf::StripPrefixString;
42 using google::protobuf::StripString;
43 using google::protobuf::StripSuffixString;
44 using google::protobuf::UpperString;
45
46 namespace {
47
48 // Keep this value in sync with ProtoDecoder::kMaxDecoderFieldId. If they go out
49 // of sync pbzero.h files will stop compiling, hitting the at() static_assert.
50 // Not worth an extra dependency.
51 constexpr int kMaxDecoderFieldId = 999;
52
Assert(bool condition)53 void Assert(bool condition) {
54 if (!condition)
55 __builtin_trap();
56 }
57
58 struct FileDescriptorComp {
operator ()protozero::__anone8b65b2e0111::FileDescriptorComp59 bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
60 int comp = lhs->name().compare(rhs->name());
61 Assert(comp != 0 || lhs == rhs);
62 return comp < 0;
63 }
64 };
65
66 struct DescriptorComp {
operator ()protozero::__anone8b65b2e0111::DescriptorComp67 bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
68 int comp = lhs->full_name().compare(rhs->full_name());
69 Assert(comp != 0 || lhs == rhs);
70 return comp < 0;
71 }
72 };
73
74 struct EnumDescriptorComp {
operator ()protozero::__anone8b65b2e0111::EnumDescriptorComp75 bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
76 int comp = lhs->full_name().compare(rhs->full_name());
77 Assert(comp != 0 || lhs == rhs);
78 return comp < 0;
79 }
80 };
81
ProtoStubName(const FileDescriptor * proto)82 inline std::string ProtoStubName(const FileDescriptor* proto) {
83 return StripSuffixString(proto->name(), ".proto") + ".pbzero";
84 }
85
86 class GeneratorJob {
87 public:
GeneratorJob(const FileDescriptor * file,Printer * stub_h_printer)88 GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
89 : source_(file), stub_h_(stub_h_printer) {}
90
GenerateStubs()91 bool GenerateStubs() {
92 Preprocess();
93 GeneratePrologue();
94 for (const EnumDescriptor* enumeration : enums_)
95 GenerateEnumDescriptor(enumeration);
96 for (const Descriptor* message : messages_)
97 GenerateMessageDescriptor(message);
98 GenerateEpilogue();
99 return error_.empty();
100 }
101
SetOption(const std::string & name,const std::string & value)102 void SetOption(const std::string& name, const std::string& value) {
103 if (name == "wrapper_namespace") {
104 wrapper_namespace_ = value;
105 } else {
106 Abort(std::string() + "Unknown plugin option '" + name + "'.");
107 }
108 }
109
110 // If generator fails to produce stubs for a particular proto definitions
111 // it finishes with undefined output and writes the first error occured.
GetFirstError() const112 const std::string& GetFirstError() const { return error_; }
113
114 private:
115 // Only the first error will be recorded.
Abort(const std::string & reason)116 void Abort(const std::string& reason) {
117 if (error_.empty())
118 error_ = reason;
119 }
120
121 // Get full name (including outer descriptors) of proto descriptor.
122 template <class T>
GetDescriptorName(const T * descriptor)123 inline std::string GetDescriptorName(const T* descriptor) {
124 if (!package_.empty()) {
125 return StripPrefixString(descriptor->full_name(), package_ + ".");
126 } else {
127 return descriptor->full_name();
128 }
129 }
130
131 // Get C++ class name corresponding to proto descriptor.
132 // Nested names are splitted by underscores. Underscores in type names aren't
133 // prohibited but not recommended in order to avoid name collisions.
134 template <class T>
GetCppClassName(const T * descriptor,bool full=false)135 inline std::string GetCppClassName(const T* descriptor, bool full = false) {
136 std::string name = GetDescriptorName(descriptor);
137 StripString(&name, ".", '_');
138 if (full)
139 name = full_namespace_prefix_ + name;
140 return name;
141 }
142
GetFieldNumberConstant(const FieldDescriptor * field)143 inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
144 std::string name = field->camelcase_name();
145 if (!name.empty()) {
146 name.at(0) = static_cast<char>(toupper(name.at(0)));
147 name = "k" + name + "FieldNumber";
148 } else {
149 // Protoc allows fields like 'bool _ = 1'.
150 Abort("Empty field name in camel case notation.");
151 }
152 return name;
153 }
154
155 // Small enums can be written faster without involving VarInt encoder.
IsTinyEnumField(const FieldDescriptor * field)156 inline bool IsTinyEnumField(const FieldDescriptor* field) {
157 if (field->type() != FieldDescriptor::TYPE_ENUM)
158 return false;
159 const EnumDescriptor* enumeration = field->enum_type();
160
161 for (int i = 0; i < enumeration->value_count(); ++i) {
162 int32_t value = enumeration->value(i)->number();
163 if (value < 0 || value > 0x7F)
164 return false;
165 }
166 return true;
167 }
168
CollectDescriptors()169 void CollectDescriptors() {
170 // Collect message descriptors in DFS order.
171 std::vector<const Descriptor*> stack;
172 for (int i = 0; i < source_->message_type_count(); ++i)
173 stack.push_back(source_->message_type(i));
174
175 while (!stack.empty()) {
176 const Descriptor* message = stack.back();
177 stack.pop_back();
178 messages_.push_back(message);
179 for (int i = 0; i < message->nested_type_count(); ++i) {
180 stack.push_back(message->nested_type(i));
181 }
182 }
183
184 // Collect enums.
185 for (int i = 0; i < source_->enum_type_count(); ++i)
186 enums_.push_back(source_->enum_type(i));
187
188 for (const Descriptor* message : messages_) {
189 for (int i = 0; i < message->enum_type_count(); ++i) {
190 enums_.push_back(message->enum_type(i));
191 }
192 }
193 }
194
CollectDependencies()195 void CollectDependencies() {
196 // Public import basically means that callers only need to import this
197 // proto in order to use the stuff publicly imported by this proto.
198 for (int i = 0; i < source_->public_dependency_count(); ++i)
199 public_imports_.insert(source_->public_dependency(i));
200
201 if (source_->weak_dependency_count() > 0)
202 Abort("Weak imports are not supported.");
203
204 // Sanity check. Collect public imports (of collected imports) in DFS order.
205 // Visibilty for current proto:
206 // - all imports listed in current proto,
207 // - public imports of everything imported (recursive).
208 std::vector<const FileDescriptor*> stack;
209 for (int i = 0; i < source_->dependency_count(); ++i) {
210 const FileDescriptor* import = source_->dependency(i);
211 stack.push_back(import);
212 if (public_imports_.count(import) == 0) {
213 private_imports_.insert(import);
214 }
215 }
216
217 while (!stack.empty()) {
218 const FileDescriptor* import = stack.back();
219 stack.pop_back();
220 // Having imports under different packages leads to unnecessary
221 // complexity with namespaces.
222 if (import->package() != package_)
223 Abort("Imported proto must be in the same package.");
224
225 for (int i = 0; i < import->public_dependency_count(); ++i) {
226 stack.push_back(import->public_dependency(i));
227 }
228 }
229
230 // Collect descriptors of messages and enums used in current proto.
231 // It will be used to generate necessary forward declarations and performed
232 // sanity check guarantees that everything lays in the same namespace.
233 for (const Descriptor* message : messages_) {
234 for (int i = 0; i < message->field_count(); ++i) {
235 const FieldDescriptor* field = message->field(i);
236
237 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
238 if (public_imports_.count(field->message_type()->file()) == 0) {
239 // Avoid multiple forward declarations since
240 // public imports have been already included.
241 referenced_messages_.insert(field->message_type());
242 }
243 } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
244 if (public_imports_.count(field->enum_type()->file()) == 0) {
245 referenced_enums_.insert(field->enum_type());
246 }
247 }
248 }
249 }
250 }
251
Preprocess()252 void Preprocess() {
253 // Package name maps to a series of namespaces.
254 package_ = source_->package();
255 namespaces_ = Split(package_, ".");
256 if (!wrapper_namespace_.empty())
257 namespaces_.push_back(wrapper_namespace_);
258
259 full_namespace_prefix_ = "::";
260 for (const std::string& ns : namespaces_)
261 full_namespace_prefix_ += ns + "::";
262
263 CollectDescriptors();
264 CollectDependencies();
265 }
266
267 // Print top header, namespaces and forward declarations.
GeneratePrologue()268 void GeneratePrologue() {
269 std::string greeting =
270 "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
271 std::string guard = package_ + "_" + source_->name() + "_H_";
272 UpperString(&guard);
273 StripString(&guard, ".-/\\", '_');
274
275 stub_h_->Print(
276 "$greeting$\n"
277 "#ifndef $guard$\n"
278 "#define $guard$\n\n"
279 "#include <stddef.h>\n"
280 "#include <stdint.h>\n\n"
281 "#include \"perfetto/base/export.h\"\n"
282 "#include \"perfetto/protozero/proto_decoder.h\"\n"
283 "#include \"perfetto/protozero/message.h\"\n",
284 "greeting", greeting, "guard", guard);
285
286 // Print includes for public imports.
287 for (const FileDescriptor* dependency : public_imports_) {
288 // Dependency name could contain slashes but importing from upper-level
289 // directories is not possible anyway since build system processes each
290 // proto file individually. Hence proto lookup path is always equal to the
291 // directory where particular proto file is located and protoc does not
292 // allow reference to upper directory (aka ..) in import path.
293 //
294 // Laconically said:
295 // - source_->name() may never have slashes,
296 // - dependency->name() may have slashes but always refers to inner path.
297 stub_h_->Print("#include \"$name$.h\"\n", "name",
298 ProtoStubName(dependency));
299 }
300 stub_h_->Print("\n");
301
302 // Print namespaces.
303 for (const std::string& ns : namespaces_) {
304 stub_h_->Print("namespace $ns$ {\n", "ns", ns);
305 }
306 stub_h_->Print("\n");
307
308 // Print forward declarations.
309 for (const Descriptor* message : referenced_messages_) {
310 stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
311 }
312 for (const EnumDescriptor* enumeration : referenced_enums_) {
313 stub_h_->Print("enum $class$ : int32_t;\n", "class",
314 GetCppClassName(enumeration));
315 }
316 stub_h_->Print("\n");
317 }
318
GenerateEnumDescriptor(const EnumDescriptor * enumeration)319 void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
320 stub_h_->Print("enum $class$ : int32_t {\n", "class",
321 GetCppClassName(enumeration));
322 stub_h_->Indent();
323
324 std::string value_name_prefix;
325 if (enumeration->containing_type() != nullptr)
326 value_name_prefix = GetCppClassName(enumeration) + "_";
327
328 for (int i = 0; i < enumeration->value_count(); ++i) {
329 const EnumValueDescriptor* value = enumeration->value(i);
330 stub_h_->Print("$name$ = $number$,\n", "name",
331 value_name_prefix + value->name(), "number",
332 std::to_string(value->number()));
333 }
334
335 stub_h_->Outdent();
336 stub_h_->Print("};\n\n");
337 }
338
GenerateSimpleFieldDescriptor(const FieldDescriptor * field)339 void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
340 std::map<std::string, std::string> setter;
341 setter["id"] = std::to_string(field->number());
342 setter["name"] = field->name();
343 setter["action"] = field->is_repeated() ? "add" : "set";
344
345 std::string appender;
346 std::string cpp_type;
347
348 switch (field->type()) {
349 case FieldDescriptor::TYPE_BOOL: {
350 appender = "AppendTinyVarInt";
351 cpp_type = "bool";
352 break;
353 }
354 case FieldDescriptor::TYPE_INT32: {
355 appender = "AppendVarInt";
356 cpp_type = "int32_t";
357 break;
358 }
359 case FieldDescriptor::TYPE_INT64: {
360 appender = "AppendVarInt";
361 cpp_type = "int64_t";
362 break;
363 }
364 case FieldDescriptor::TYPE_UINT32: {
365 appender = "AppendVarInt";
366 cpp_type = "uint32_t";
367 break;
368 }
369 case FieldDescriptor::TYPE_UINT64: {
370 appender = "AppendVarInt";
371 cpp_type = "uint64_t";
372 break;
373 }
374 case FieldDescriptor::TYPE_SINT32: {
375 appender = "AppendSignedVarInt";
376 cpp_type = "int32_t";
377 break;
378 }
379 case FieldDescriptor::TYPE_SINT64: {
380 appender = "AppendSignedVarInt";
381 cpp_type = "int64_t";
382 break;
383 }
384 case FieldDescriptor::TYPE_FIXED32: {
385 appender = "AppendFixed";
386 cpp_type = "uint32_t";
387 break;
388 }
389 case FieldDescriptor::TYPE_FIXED64: {
390 appender = "AppendFixed";
391 cpp_type = "uint64_t";
392 break;
393 }
394 case FieldDescriptor::TYPE_SFIXED32: {
395 appender = "AppendFixed";
396 cpp_type = "int32_t";
397 break;
398 }
399 case FieldDescriptor::TYPE_SFIXED64: {
400 appender = "AppendFixed";
401 cpp_type = "int64_t";
402 break;
403 }
404 case FieldDescriptor::TYPE_FLOAT: {
405 appender = "AppendFixed";
406 cpp_type = "float";
407 break;
408 }
409 case FieldDescriptor::TYPE_DOUBLE: {
410 appender = "AppendFixed";
411 cpp_type = "double";
412 break;
413 }
414 case FieldDescriptor::TYPE_ENUM: {
415 appender = IsTinyEnumField(field) ? "AppendTinyVarInt" : "AppendVarInt";
416 cpp_type = GetCppClassName(field->enum_type(), true);
417 break;
418 }
419 case FieldDescriptor::TYPE_STRING: {
420 appender = "AppendString";
421 cpp_type = "const char*";
422 break;
423 }
424 case FieldDescriptor::TYPE_BYTES: {
425 stub_h_->Print(
426 setter,
427 "void $action$_$name$(const uint8_t* data, size_t size) {\n"
428 " AppendBytes($id$, data, size);\n"
429 "}\n");
430 return;
431 }
432 case FieldDescriptor::TYPE_GROUP:
433 case FieldDescriptor::TYPE_MESSAGE: {
434 Abort("Unsupported field type.");
435 return;
436 }
437 }
438 setter["appender"] = appender;
439 setter["cpp_type"] = cpp_type;
440 stub_h_->Print(setter,
441 "void $action$_$name$($cpp_type$ value) {\n"
442 " $appender$($id$, value);\n"
443 "}\n");
444
445 // For strings also generate a variant for non-null terminated strings.
446 if (field->type() == FieldDescriptor::TYPE_STRING) {
447 stub_h_->Print(setter,
448 "// Doesn't check for null terminator.\n"
449 "// Expects |value| to be at least |size| long.\n"
450 "void $action$_$name$($cpp_type$ value, size_t size) {\n"
451 " AppendBytes($id$, value, size);\n"
452 "}\n");
453 }
454 }
455
GenerateNestedMessageFieldDescriptor(const FieldDescriptor * field)456 void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
457 std::string action = field->is_repeated() ? "add" : "set";
458 std::string inner_class = GetCppClassName(field->message_type());
459 stub_h_->Print(
460 "template <typename T = $inner_class$> T* $action$_$name$() {\n"
461 " return BeginNestedMessage<T>($id$);\n"
462 "}\n\n",
463 "id", std::to_string(field->number()), "name", field->name(), "action",
464 action, "inner_class", inner_class);
465 }
466
GenerateDecoder(const Descriptor * message)467 void GenerateDecoder(const Descriptor* message) {
468 int max_field_id = 0;
469 bool has_repeated_fields = false;
470 for (int i = 0; i < message->field_count(); ++i) {
471 const FieldDescriptor* field = message->field(i);
472 if (field->number() > kMaxDecoderFieldId)
473 continue;
474 max_field_id = std::max(max_field_id, field->number());
475 if (field->is_repeated())
476 has_repeated_fields = true;
477 }
478
479 stub_h_->Print(
480 "class Decoder : public "
481 "::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/$max$, "
482 "/*HAS_REPEATED_FIELDS=*/$rep$> {\n",
483 "max", std::to_string(max_field_id), "rep",
484 has_repeated_fields ? "true" : "false");
485 stub_h_->Print(" public:\n");
486 stub_h_->Indent();
487 stub_h_->Print(
488 "Decoder(const uint8_t* data, size_t len) "
489 ": TypedProtoDecoder(data, len) {}\n");
490
491 for (int i = 0; i < message->field_count(); ++i) {
492 const FieldDescriptor* field = message->field(i);
493 if (field->is_packed()) {
494 Abort("Packed repeated fields are not supported.");
495 return;
496 }
497
498 if (field->number() > max_field_id) {
499 stub_h_->Print("// field $name$ omitted because its id is too high\n",
500 "name", field->name());
501 continue;
502 }
503 std::string getter;
504 std::string cpp_type;
505 switch (field->type()) {
506 case FieldDescriptor::TYPE_BOOL:
507 getter = "as_bool";
508 cpp_type = "bool";
509 break;
510 case FieldDescriptor::TYPE_SFIXED32:
511 case FieldDescriptor::TYPE_SINT32:
512 case FieldDescriptor::TYPE_INT32:
513 getter = "as_int32";
514 cpp_type = "int32_t";
515 break;
516 case FieldDescriptor::TYPE_SFIXED64:
517 case FieldDescriptor::TYPE_SINT64:
518 case FieldDescriptor::TYPE_INT64:
519 getter = "as_int64";
520 cpp_type = "int64_t";
521 break;
522 case FieldDescriptor::TYPE_FIXED32:
523 case FieldDescriptor::TYPE_UINT32:
524 getter = "as_uint32";
525 cpp_type = "uint32_t";
526 break;
527 case FieldDescriptor::TYPE_FIXED64:
528 case FieldDescriptor::TYPE_UINT64:
529 getter = "as_uint64";
530 cpp_type = "uint64_t";
531 break;
532 case FieldDescriptor::TYPE_FLOAT:
533 getter = "as_float";
534 cpp_type = "float";
535 break;
536 case FieldDescriptor::TYPE_DOUBLE:
537 getter = "as_double";
538 cpp_type = "double";
539 break;
540 case FieldDescriptor::TYPE_ENUM:
541 getter = "as_int32";
542 cpp_type = "int32_t";
543 break;
544 case FieldDescriptor::TYPE_STRING:
545 getter = "as_string";
546 cpp_type = "::protozero::ConstChars";
547 break;
548 case FieldDescriptor::TYPE_MESSAGE:
549 case FieldDescriptor::TYPE_BYTES:
550 getter = "as_bytes";
551 cpp_type = "::protozero::ConstBytes";
552 break;
553 case FieldDescriptor::TYPE_GROUP:
554 continue;
555 }
556
557 stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
558 "name", field->name(), "id",
559 std::to_string(field->number()));
560
561 if (field->is_repeated()) {
562 stub_h_->Print(
563 "::protozero::RepeatedFieldIterator $name$() const { return "
564 "GetRepeated($id$); }\n",
565 "name", field->name(), "id", std::to_string(field->number()));
566 } else {
567 stub_h_->Print(
568 "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
569 "name", field->name(), "id", std::to_string(field->number()),
570 "cpp_type", cpp_type, "getter", getter);
571 }
572 }
573 stub_h_->Outdent();
574 stub_h_->Print("};\n");
575 }
576
GenerateConstantsForMessageFields(const Descriptor * message)577 void GenerateConstantsForMessageFields(const Descriptor* message) {
578 const bool has_fields = (message->field_count() > 0);
579
580 // Field number constants.
581 if (has_fields) {
582 stub_h_->Print("enum : int32_t {\n");
583 stub_h_->Indent();
584
585 for (int i = 0; i < message->field_count(); ++i) {
586 const FieldDescriptor* field = message->field(i);
587 stub_h_->Print("$name$ = $id$,\n", "name",
588 GetFieldNumberConstant(field), "id",
589 std::to_string(field->number()));
590 }
591 stub_h_->Outdent();
592 stub_h_->Print("};\n");
593 }
594 }
595
GenerateMessageDescriptor(const Descriptor * message)596 void GenerateMessageDescriptor(const Descriptor* message) {
597 stub_h_->Print(
598 "class PERFETTO_EXPORT $name$ : public ::protozero::Message {\n"
599 " public:\n",
600 "name", GetCppClassName(message));
601 stub_h_->Indent();
602
603 GenerateConstantsForMessageFields(message);
604 GenerateDecoder(message);
605
606 // Using statements for nested messages.
607 for (int i = 0; i < message->nested_type_count(); ++i) {
608 const Descriptor* nested_message = message->nested_type(i);
609 stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
610 nested_message->name(), "global_name",
611 GetCppClassName(nested_message, true));
612 }
613
614 // Using statements for nested enums.
615 for (int i = 0; i < message->enum_type_count(); ++i) {
616 const EnumDescriptor* nested_enum = message->enum_type(i);
617 stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
618 nested_enum->name(), "global_name",
619 GetCppClassName(nested_enum, true));
620 }
621
622 // Values of nested enums.
623 for (int i = 0; i < message->enum_type_count(); ++i) {
624 const EnumDescriptor* nested_enum = message->enum_type(i);
625 std::string value_name_prefix = GetCppClassName(nested_enum) + "_";
626
627 for (int j = 0; j < nested_enum->value_count(); ++j) {
628 const EnumValueDescriptor* value = nested_enum->value(j);
629 stub_h_->Print("static const $class$ $name$ = $full_name$;\n", "class",
630 nested_enum->name(), "name", value->name(), "full_name",
631 value_name_prefix + value->name());
632 }
633 }
634
635 // Field descriptors.
636 for (int i = 0; i < message->field_count(); ++i) {
637 const FieldDescriptor* field = message->field(i);
638 if (field->is_packed()) {
639 Abort("Packed repeated fields are not supported.");
640 return;
641 }
642 if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
643 GenerateSimpleFieldDescriptor(field);
644 } else {
645 GenerateNestedMessageFieldDescriptor(field);
646 }
647 }
648
649 stub_h_->Outdent();
650 stub_h_->Print("};\n\n");
651 }
652
GenerateEpilogue()653 void GenerateEpilogue() {
654 for (unsigned i = 0; i < namespaces_.size(); ++i) {
655 stub_h_->Print("} // Namespace.\n");
656 }
657 stub_h_->Print("#endif // Include guard.\n");
658 }
659
660 const FileDescriptor* const source_;
661 Printer* const stub_h_;
662 std::string error_;
663
664 std::string package_;
665 std::string wrapper_namespace_;
666 std::vector<std::string> namespaces_;
667 std::string full_namespace_prefix_;
668 std::vector<const Descriptor*> messages_;
669 std::vector<const EnumDescriptor*> enums_;
670
671 // The custom *Comp comparators are to ensure determinism of the generator.
672 std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
673 std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
674 std::set<const Descriptor*, DescriptorComp> referenced_messages_;
675 std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
676 };
677
678 } // namespace
679
ProtoZeroGenerator()680 ProtoZeroGenerator::ProtoZeroGenerator() {}
681
~ProtoZeroGenerator()682 ProtoZeroGenerator::~ProtoZeroGenerator() {}
683
Generate(const FileDescriptor * file,const std::string & options,GeneratorContext * context,std::string * error) const684 bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
685 const std::string& options,
686 GeneratorContext* context,
687 std::string* error) const {
688 const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
689 context->Open(ProtoStubName(file) + ".h"));
690 const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
691 context->Open(ProtoStubName(file) + ".cc"));
692
693 // Variables are delimited by $.
694 Printer stub_h_printer(stub_h_file_stream.get(), '$');
695 GeneratorJob job(file, &stub_h_printer);
696
697 Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
698 stub_cc_printer.Print("// Intentionally empty\n");
699
700 // Parse additional options.
701 for (const std::string& option : Split(options, ",")) {
702 std::vector<std::string> option_pair = Split(option, "=");
703 job.SetOption(option_pair[0], option_pair[1]);
704 }
705
706 if (!job.GenerateStubs()) {
707 *error = job.GetFirstError();
708 return false;
709 }
710 return true;
711 }
712
713 } // namespace protozero
714