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