• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/compiler/objectivec/objectivec_file.h>
32 #include <google/protobuf/compiler/objectivec/objectivec_enum.h>
33 #include <google/protobuf/compiler/objectivec/objectivec_extension.h>
34 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
35 #include <google/protobuf/compiler/objectivec/objectivec_message.h>
36 #include <google/protobuf/compiler/code_generator.h>
37 #include <google/protobuf/io/printer.h>
38 #include <google/protobuf/io/zero_copy_stream_impl.h>
39 #include <google/protobuf/stubs/stl_util.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <algorithm> // std::find()
42 #include <iostream>
43 #include <sstream>
44 
45 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
46 // error cases, so it seems to be ok to use as a back door for errors.
47 
48 namespace google {
49 namespace protobuf {
50 namespace compiler {
51 namespace objectivec {
52 
53 namespace {
54 
55 // This is also found in GPBBootstrap.h, and needs to be kept in sync.
56 const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30004;
57 
58 const char* kHeaderExtension = ".pbobjc.h";
59 
BundledFileName(const FileDescriptor * file)60 std::string BundledFileName(const FileDescriptor* file) {
61   return "GPB" + FilePathBasename(file) + kHeaderExtension;
62 }
63 
64 // Checks if a message contains any enums definitions (on the message or
65 // a nested message under it).
MessageContainsEnums(const Descriptor * message)66 bool MessageContainsEnums(const Descriptor* message) {
67   if (message->enum_type_count() > 0) {
68     return true;
69   }
70   for (int i = 0; i < message->nested_type_count(); i++) {
71     if (MessageContainsEnums(message->nested_type(i))) {
72       return true;
73     }
74   }
75   return false;
76 }
77 
78 // Checks if a message contains any extension definitions (on the message or
79 // a nested message under it).
MessageContainsExtensions(const Descriptor * message)80 bool MessageContainsExtensions(const Descriptor* message) {
81   if (message->extension_count() > 0) {
82     return true;
83   }
84   for (int i = 0; i < message->nested_type_count(); i++) {
85     if (MessageContainsExtensions(message->nested_type(i))) {
86       return true;
87     }
88   }
89   return false;
90 }
91 
92 // Checks if the file contains any enum definitions (at the root or
93 // nested under a message).
FileContainsEnums(const FileDescriptor * file)94 bool FileContainsEnums(const FileDescriptor* file) {
95   if (file->enum_type_count() > 0) {
96     return true;
97   }
98   for (int i = 0; i < file->message_type_count(); i++) {
99     if (MessageContainsEnums(file->message_type(i))) {
100       return true;
101     }
102   }
103   return false;
104 }
105 
106 // Checks if the file contains any extensions definitions (at the root or
107 // nested under a message).
FileContainsExtensions(const FileDescriptor * file)108 bool FileContainsExtensions(const FileDescriptor* file) {
109   if (file->extension_count() > 0) {
110     return true;
111   }
112   for (int i = 0; i < file->message_type_count(); i++) {
113     if (MessageContainsExtensions(file->message_type(i))) {
114       return true;
115     }
116   }
117   return false;
118 }
119 
120 // Helper for CollectMinimalFileDepsContainingExtensionsWorker that marks all
121 // deps as visited and prunes them from the needed files list.
PruneFileAndDepsMarkingAsVisited(const FileDescriptor * file,std::vector<const FileDescriptor * > * files,std::set<const FileDescriptor * > * files_visited)122 void PruneFileAndDepsMarkingAsVisited(
123     const FileDescriptor* file,
124     std::vector<const FileDescriptor*>* files,
125     std::set<const FileDescriptor*>* files_visited) {
126   std::vector<const FileDescriptor*>::iterator iter =
127       std::find(files->begin(), files->end(), file);
128   if (iter != files->end()) {
129     files->erase(iter);
130   }
131   files_visited->insert(file);
132   for (int i = 0; i < file->dependency_count(); i++) {
133     PruneFileAndDepsMarkingAsVisited(file->dependency(i), files, files_visited);
134   }
135 }
136 
137 // Helper for CollectMinimalFileDepsContainingExtensions.
CollectMinimalFileDepsContainingExtensionsWorker(const FileDescriptor * file,std::vector<const FileDescriptor * > * files,std::set<const FileDescriptor * > * files_visited)138 void CollectMinimalFileDepsContainingExtensionsWorker(
139     const FileDescriptor* file,
140     std::vector<const FileDescriptor*>* files,
141     std::set<const FileDescriptor*>* files_visited) {
142   if (files_visited->find(file) != files_visited->end()) {
143     return;
144   }
145   files_visited->insert(file);
146 
147   if (FileContainsExtensions(file)) {
148     files->push_back(file);
149     for (int i = 0; i < file->dependency_count(); i++) {
150       const FileDescriptor* dep = file->dependency(i);
151       PruneFileAndDepsMarkingAsVisited(dep, files, files_visited);
152     }
153   } else {
154     for (int i = 0; i < file->dependency_count(); i++) {
155       const FileDescriptor* dep = file->dependency(i);
156       CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
157                                                        files_visited);
158     }
159   }
160 }
161 
162 // Collect the deps of the given file that contain extensions. This can be used to
163 // create the chain of roots that need to be wired together.
164 //
165 // NOTE: If any changes are made to this and the supporting functions, you will
166 // need to manually validate what the generated code is for the test files:
167 //   objectivec/Tests/unittest_extension_chain_*.proto
168 // There are comments about what the expected code should be line and limited
169 // testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
170 // specifically).
CollectMinimalFileDepsContainingExtensions(const FileDescriptor * file,std::vector<const FileDescriptor * > * files)171 void CollectMinimalFileDepsContainingExtensions(
172     const FileDescriptor* file,
173     std::vector<const FileDescriptor*>* files) {
174   std::set<const FileDescriptor*> files_visited;
175   for (int i = 0; i < file->dependency_count(); i++) {
176     const FileDescriptor* dep = file->dependency(i);
177     CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
178                                                      &files_visited);
179   }
180 }
181 
IsDirectDependency(const FileDescriptor * dep,const FileDescriptor * file)182 bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
183   for (int i = 0; i < file->dependency_count(); i++) {
184     if (dep == file->dependency(i)) {
185       return true;
186     }
187   }
188   return false;
189 }
190 
191 }  // namespace
192 
FileGenerator(const FileDescriptor * file,const GenerationOptions & generation_options)193 FileGenerator::FileGenerator(const FileDescriptor* file,
194                              const GenerationOptions& generation_options)
195     : file_(file),
196       generation_options_(generation_options),
197       root_class_name_(FileClassName(file)),
198       is_bundled_proto_(IsProtobufLibraryBundledProtoFile(file)) {
199   for (int i = 0; i < file_->enum_type_count(); i++) {
200     EnumGenerator* generator = new EnumGenerator(file_->enum_type(i));
201     enum_generators_.emplace_back(generator);
202   }
203   for (int i = 0; i < file_->message_type_count(); i++) {
204     MessageGenerator* generator =
205         new MessageGenerator(root_class_name_, file_->message_type(i));
206     message_generators_.emplace_back(generator);
207   }
208   for (int i = 0; i < file_->extension_count(); i++) {
209     ExtensionGenerator* generator =
210         new ExtensionGenerator(root_class_name_, file_->extension(i));
211     extension_generators_.emplace_back(generator);
212   }
213 }
214 
~FileGenerator()215 FileGenerator::~FileGenerator() {}
216 
GenerateHeader(io::Printer * printer)217 void FileGenerator::GenerateHeader(io::Printer* printer) {
218   std::vector<std::string> headers;
219   // Generated files bundled with the library get minimal imports, everything
220   // else gets the wrapper so everything is usable.
221   if (is_bundled_proto_) {
222     headers.push_back("GPBDescriptor.h");
223     headers.push_back("GPBMessage.h");
224     headers.push_back("GPBRootObject.h");
225     for (int i = 0; i < file_->dependency_count(); i++) {
226       const std::string header_name = BundledFileName(file_->dependency(i));
227       headers.push_back(header_name);
228     }
229   } else {
230     headers.push_back("GPBProtocolBuffers.h");
231   }
232   PrintFileRuntimePreamble(printer, headers);
233 
234   // Add some verification that the generated code matches the source the
235   // code is being compiled with.
236   // NOTE: This captures the raw numeric values at the time the generator was
237   // compiled, since that will be the versions for the ObjC runtime at that
238   // time.  The constants in the generated code will then get their values at
239   // at compile time (so checking against the headers being used to compile).
240   printer->Print(
241       "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n"
242       "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n"
243       "#endif\n"
244       "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n"
245       "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n"
246       "#endif\n"
247       "\n",
248       "google_protobuf_objc_version", StrCat(GOOGLE_PROTOBUF_OBJC_VERSION));
249 
250   // The bundled protos (WKTs) don't use of forward declarations.
251   bool headers_use_forward_declarations =
252       generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
253 
254   {
255     ImportWriter import_writer(
256         generation_options_.generate_for_named_framework,
257         generation_options_.named_framework_to_proto_path_mappings_path,
258         generation_options_.runtime_import_prefix,
259         /* include_wkt_imports = */ false);
260     const std::string header_extension(kHeaderExtension);
261     if (headers_use_forward_declarations) {
262       // #import any headers for "public imports" in the proto file.
263       for (int i = 0; i < file_->public_dependency_count(); i++) {
264         import_writer.AddFile(file_->public_dependency(i), header_extension);
265       }
266     } else {
267       for (int i = 0; i < file_->dependency_count(); i++) {
268         import_writer.AddFile(file_->dependency(i), header_extension);
269       }
270     }
271     import_writer.Print(printer);
272   }
273 
274   // Note:
275   //  deprecated-declarations suppression is only needed if some place in this
276   //    proto file is something deprecated or if it references something from
277   //    another file that is deprecated.
278   printer->Print(
279       "// @@protoc_insertion_point(imports)\n"
280       "\n"
281       "#pragma clang diagnostic push\n"
282       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
283       "\n"
284       "CF_EXTERN_C_BEGIN\n"
285       "\n");
286 
287   std::set<std::string> fwd_decls;
288   for (const auto& generator : message_generators_) {
289     generator->DetermineForwardDeclarations(
290         &fwd_decls,
291         /* include_external_types = */ headers_use_forward_declarations);
292   }
293   for (std::set<std::string>::const_iterator i(fwd_decls.begin());
294        i != fwd_decls.end(); ++i) {
295     printer->Print("$value$;\n", "value", *i);
296   }
297   if (fwd_decls.begin() != fwd_decls.end()) {
298     printer->Print("\n");
299   }
300 
301   printer->Print(
302       "NS_ASSUME_NONNULL_BEGIN\n"
303       "\n");
304 
305   // need to write out all enums first
306   for (const auto& generator : enum_generators_) {
307     generator->GenerateHeader(printer);
308   }
309 
310   for (const auto& generator : message_generators_) {
311     generator->GenerateEnumHeader(printer);
312   }
313 
314   // For extensions to chain together, the Root gets created even if there
315   // are no extensions.
316   printer->Print(
317       "#pragma mark - $root_class_name$\n"
318       "\n"
319       "/**\n"
320       " * Exposes the extension registry for this file.\n"
321       " *\n"
322       " * The base class provides:\n"
323       " * @code\n"
324       " *   + (GPBExtensionRegistry *)extensionRegistry;\n"
325       " * @endcode\n"
326       " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
327       " * this file and all files that it depends on.\n"
328       " **/\n"
329       "GPB_FINAL @interface $root_class_name$ : GPBRootObject\n"
330       "@end\n"
331       "\n",
332       "root_class_name", root_class_name_);
333 
334   if (!extension_generators_.empty()) {
335     // The dynamic methods block is only needed if there are extensions.
336     printer->Print(
337         "@interface $root_class_name$ (DynamicMethods)\n",
338         "root_class_name", root_class_name_);
339 
340     for (const auto& generator : extension_generators_) {
341       generator->GenerateMembersHeader(printer);
342     }
343 
344     printer->Print("@end\n\n");
345   }  // !extension_generators_.empty()
346 
347   for (const auto& generator : message_generators_) {
348     generator->GenerateMessageHeader(printer);
349   }
350 
351   printer->Print(
352       "NS_ASSUME_NONNULL_END\n"
353       "\n"
354       "CF_EXTERN_C_END\n"
355       "\n"
356       "#pragma clang diagnostic pop\n"
357       "\n"
358       "// @@protoc_insertion_point(global_scope)\n");
359 }
360 
GenerateSource(io::Printer * printer)361 void FileGenerator::GenerateSource(io::Printer* printer) {
362   // #import the runtime support.
363   std::vector<std::string> headers;
364   headers.push_back("GPBProtocolBuffers_RuntimeSupport.h");
365   if (is_bundled_proto_) {
366     headers.push_back(BundledFileName(file_));
367   }
368   PrintFileRuntimePreamble(printer, headers);
369 
370   // Enums use atomic in the generated code, so add the system import as needed.
371   if (FileContainsEnums(file_)) {
372     printer->Print(
373         "#import <stdatomic.h>\n"
374         "\n");
375   }
376 
377   std::vector<const FileDescriptor*> deps_with_extensions;
378   CollectMinimalFileDepsContainingExtensions(file_, &deps_with_extensions);
379 
380   // The bundled protos (WKTs) don't use of forward declarations.
381   bool headers_use_forward_declarations =
382       generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
383 
384   {
385     ImportWriter import_writer(
386         generation_options_.generate_for_named_framework,
387         generation_options_.named_framework_to_proto_path_mappings_path,
388         generation_options_.runtime_import_prefix,
389         /* include_wkt_imports = */ false);
390     const std::string header_extension(kHeaderExtension);
391 
392     // #import the header for this proto file.
393     import_writer.AddFile(file_, header_extension);
394 
395     if (headers_use_forward_declarations) {
396       // #import the headers for anything that a plain dependency of this proto
397       // file (that means they were just an include, not a "public" include).
398       std::set<std::string> public_import_names;
399       for (int i = 0; i < file_->public_dependency_count(); i++) {
400         public_import_names.insert(file_->public_dependency(i)->name());
401       }
402       for (int i = 0; i < file_->dependency_count(); i++) {
403         const FileDescriptor *dep = file_->dependency(i);
404         bool public_import = (public_import_names.count(dep->name()) != 0);
405         if (!public_import) {
406           import_writer.AddFile(dep, header_extension);
407         }
408       }
409     }
410 
411     // If any indirect dependency provided extensions, it needs to be directly
412     // imported so it can get merged into the root's extensions registry.
413     // See the Note by CollectMinimalFileDepsContainingExtensions before
414     // changing this.
415     for (std::vector<const FileDescriptor*>::iterator iter =
416              deps_with_extensions.begin();
417          iter != deps_with_extensions.end(); ++iter) {
418       if (!IsDirectDependency(*iter, file_)) {
419         import_writer.AddFile(*iter, header_extension);
420       }
421     }
422 
423     import_writer.Print(printer);
424   }
425 
426   bool includes_oneof = false;
427   for (const auto& generator : message_generators_) {
428     if (generator->IncludesOneOfDefinition()) {
429       includes_oneof = true;
430       break;
431     }
432   }
433 
434   std::set<std::string> fwd_decls;
435   for (const auto& generator : message_generators_) {
436     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
437   }
438   for (const auto& generator : extension_generators_) {
439     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
440   }
441 
442   // Note:
443   //  deprecated-declarations suppression is only needed if some place in this
444   //    proto file is something deprecated or if it references something from
445   //    another file that is deprecated.
446   //  dollar-in-identifier-extension is needed because we use references to
447   //    objc class names that have $ in identifiers.
448   printer->Print(
449       "// @@protoc_insertion_point(imports)\n"
450       "\n"
451       "#pragma clang diagnostic push\n"
452       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
453   if (includes_oneof) {
454     // The generated code for oneof's uses direct ivar access, suppress the
455     // warning in case developer turn that on in the context they compile the
456     // generated code.
457     printer->Print(
458         "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
459   }
460   if (!fwd_decls.empty()) {
461     printer->Print(
462       "#pragma clang diagnostic ignored \"-Wdollar-in-identifier-extension\"\n");
463   }
464   printer->Print(
465       "\n");
466   if (!fwd_decls.empty()) {
467     printer->Print(
468         "#pragma mark - Objective C Class declarations\n"
469         "// Forward declarations of Objective C classes that we can use as\n"
470         "// static values in struct initializers.\n"
471         "// We don't use [Foo class] because it is not a static value.\n");
472   }
473   for (const auto& i : fwd_decls) {
474     printer->Print("$value$\n", "value", i);
475   }
476   if (!fwd_decls.empty()) {
477     printer->Print("\n");
478   }
479   printer->Print(
480       "#pragma mark - $root_class_name$\n"
481       "\n"
482       "@implementation $root_class_name$\n\n",
483       "root_class_name", root_class_name_);
484 
485   const bool file_contains_extensions = FileContainsExtensions(file_);
486 
487   // If there were any extensions or this file has any dependencies, output
488   // a registry to override to create the file specific registry.
489   if (file_contains_extensions || !deps_with_extensions.empty()) {
490     printer->Print(
491         "+ (GPBExtensionRegistry*)extensionRegistry {\n"
492         "  // This is called by +initialize so there is no need to worry\n"
493         "  // about thread safety and initialization of registry.\n"
494         "  static GPBExtensionRegistry* registry = nil;\n"
495         "  if (!registry) {\n"
496         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
497         "    registry = [[GPBExtensionRegistry alloc] init];\n");
498 
499     printer->Indent();
500     printer->Indent();
501 
502     if (file_contains_extensions) {
503       printer->Print(
504           "static GPBExtensionDescription descriptions[] = {\n");
505       printer->Indent();
506       for (const auto& generator : extension_generators_) {
507         generator->GenerateStaticVariablesInitialization(printer);
508       }
509       for (const auto& generator : message_generators_) {
510         generator->GenerateStaticVariablesInitialization(printer);
511       }
512       printer->Outdent();
513       printer->Print(
514           "};\n"
515           "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
516           "  GPBExtensionDescriptor *extension =\n"
517           "      [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]\n"
518           "                                                     usesClassRefs:YES];\n"
519           "  [registry addExtension:extension];\n"
520           "  [self globallyRegisterExtension:extension];\n"
521           "  [extension release];\n"
522           "}\n");
523     }
524 
525     if (deps_with_extensions.empty()) {
526       printer->Print(
527           "// None of the imports (direct or indirect) defined extensions, so no need to add\n"
528           "// them to this registry.\n");
529     } else {
530       printer->Print(
531           "// Merge in the imports (direct or indirect) that defined extensions.\n");
532       for (std::vector<const FileDescriptor*>::iterator iter =
533                deps_with_extensions.begin();
534            iter != deps_with_extensions.end(); ++iter) {
535         const std::string root_class_name(FileClassName((*iter)));
536         printer->Print(
537             "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
538             "dependency", root_class_name);
539       }
540     }
541 
542     printer->Outdent();
543     printer->Outdent();
544 
545     printer->Print(
546         "  }\n"
547         "  return registry;\n"
548         "}\n");
549   } else {
550     if (file_->dependency_count() > 0) {
551       printer->Print(
552           "// No extensions in the file and none of the imports (direct or indirect)\n"
553           "// defined extensions, so no need to generate +extensionRegistry.\n");
554     } else {
555       printer->Print(
556           "// No extensions in the file and no imports, so no need to generate\n"
557           "// +extensionRegistry.\n");
558     }
559   }
560 
561   printer->Print("\n@end\n\n");
562 
563   // File descriptor only needed if there are messages to use it.
564   if (!message_generators_.empty()) {
565     std::map<std::string, std::string> vars;
566     vars["root_class_name"] = root_class_name_;
567     vars["package"] = file_->package();
568     vars["objc_prefix"] = FileClassPrefix(file_);
569     switch (file_->syntax()) {
570       case FileDescriptor::SYNTAX_UNKNOWN:
571         vars["syntax"] = "GPBFileSyntaxUnknown";
572         break;
573       case FileDescriptor::SYNTAX_PROTO2:
574         vars["syntax"] = "GPBFileSyntaxProto2";
575         break;
576       case FileDescriptor::SYNTAX_PROTO3:
577         vars["syntax"] = "GPBFileSyntaxProto3";
578         break;
579     }
580     printer->Print(vars,
581         "#pragma mark - $root_class_name$_FileDescriptor\n"
582         "\n"
583         "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
584         "  // This is called by +initialize so there is no need to worry\n"
585         "  // about thread safety of the singleton.\n"
586         "  static GPBFileDescriptor *descriptor = NULL;\n"
587         "  if (!descriptor) {\n"
588         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
589     if (!vars["objc_prefix"].empty()) {
590       printer->Print(
591           vars,
592           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
593           "                                                 objcPrefix:@\"$objc_prefix$\"\n"
594           "                                                     syntax:$syntax$];\n");
595     } else {
596       printer->Print(
597           vars,
598           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
599           "                                                     syntax:$syntax$];\n");
600     }
601     printer->Print(
602         "  }\n"
603         "  return descriptor;\n"
604         "}\n"
605         "\n");
606   }
607 
608   for (const auto& generator : enum_generators_) {
609     generator->GenerateSource(printer);
610   }
611   for (const auto& generator : message_generators_) {
612     generator->GenerateSource(printer);
613   }
614 
615   printer->Print(
616     "\n"
617     "#pragma clang diagnostic pop\n"
618     "\n"
619     "// @@protoc_insertion_point(global_scope)\n");
620 }
621 
622 // Helper to print the import of the runtime support at the top of generated
623 // files. This currently only supports the runtime coming from a framework
624 // as defined by the official CocoaPod.
PrintFileRuntimePreamble(io::Printer * printer,const std::vector<std::string> & headers_to_import) const625 void FileGenerator::PrintFileRuntimePreamble(
626     io::Printer* printer,
627     const std::vector<std::string>& headers_to_import) const {
628   printer->Print(
629       "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
630       "// source: $filename$\n"
631       "\n",
632       "filename", file_->name());
633 
634   if (is_bundled_proto_) {
635     // This is basically a clone of ImportWriter::PrintRuntimeImports() but
636     // without the CPP symbol gate, since within the bundled files, that isn't
637     // needed.
638     std::string import_prefix = generation_options_.runtime_import_prefix;
639     if (!import_prefix.empty()) {
640       import_prefix += "/";
641     }
642     for (const auto& header : headers_to_import) {
643       printer->Print(
644           "#import \"$import_prefix$$header$\"\n",
645           "import_prefix", import_prefix,
646           "header", header);
647     }
648   } else {
649     ImportWriter::PrintRuntimeImports(
650         printer, headers_to_import, generation_options_.runtime_import_prefix, true);
651   }
652 
653   printer->Print("\n");
654 }
655 
656 }  // namespace objectivec
657 }  // namespace compiler
658 }  // namespace protobuf
659 }  // namespace google
660