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 <iostream>
41 #include <sstream>
42 
43 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
44 // error cases, so it seems to be ok to use as a back door for errors.
45 
46 namespace google {
47 namespace protobuf {
48 
49 // This is also found in GPBBootstrap.h, and needs to be kept in sync.  It
50 // is the version check done to ensure generated code works with the current
51 // runtime being used.
52 const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001;
53 
54 namespace compiler {
55 namespace objectivec {
56 
57 namespace {
58 
59 class ImportWriter {
60  public:
ImportWriter(const Options & options)61   ImportWriter(const Options& options)
62       : options_(options),
63         need_to_parse_mapping_file_(true) {}
64 
65   void AddFile(const FileGenerator* file);
66   void Print(io::Printer *printer) const;
67 
68  private:
69   class ProtoFrameworkCollector : public LineConsumer {
70    public:
ProtoFrameworkCollector(map<string,string> * inout_proto_file_to_framework_name)71     ProtoFrameworkCollector(map<string, string>* inout_proto_file_to_framework_name)
72         : map_(inout_proto_file_to_framework_name) {}
73 
74     virtual bool ConsumeLine(const StringPiece& line, string* out_error);
75 
76    private:
77     map<string, string>* map_;
78   };
79 
80   void ParseFrameworkMappings();
81 
82   const Options options_;
83   map<string, string> proto_file_to_framework_name_;
84   bool need_to_parse_mapping_file_;
85 
86   vector<string> protobuf_framework_imports_;
87   vector<string> protobuf_non_framework_imports_;
88   vector<string> other_framework_imports_;
89   vector<string> other_imports_;
90 };
91 
AddFile(const FileGenerator * file)92 void ImportWriter::AddFile(const FileGenerator* file) {
93   const FileDescriptor* file_descriptor = file->Descriptor();
94   const string extension(".pbobjc.h");
95 
96   if (IsProtobufLibraryBundledProtoFile(file_descriptor)) {
97     protobuf_framework_imports_.push_back(
98         FilePathBasename(file_descriptor) + extension);
99     protobuf_non_framework_imports_.push_back(file->Path() + extension);
100     return;
101   }
102 
103   // Lazy parse any mappings.
104   if (need_to_parse_mapping_file_) {
105     ParseFrameworkMappings();
106   }
107 
108   map<string, string>::iterator proto_lookup =
109       proto_file_to_framework_name_.find(file_descriptor->name());
110   if (proto_lookup != proto_file_to_framework_name_.end()) {
111     other_framework_imports_.push_back(
112         proto_lookup->second + "/" +
113         FilePathBasename(file_descriptor) + extension);
114     return;
115   }
116 
117   if (!options_.generate_for_named_framework.empty()) {
118     other_framework_imports_.push_back(
119         options_.generate_for_named_framework + "/" +
120         FilePathBasename(file_descriptor) + extension);
121     return;
122   }
123 
124   other_imports_.push_back(file->Path() + extension);
125 }
126 
Print(io::Printer * printer) const127 void ImportWriter::Print(io::Printer* printer) const {
128   assert(protobuf_non_framework_imports_.size() ==
129          protobuf_framework_imports_.size());
130 
131   bool add_blank_line = false;
132 
133   if (protobuf_framework_imports_.size() > 0) {
134     const string framework_name(ProtobufLibraryFrameworkName);
135     const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
136 
137     printer->Print(
138         "#if $cpp_symbol$\n",
139         "cpp_symbol", cpp_symbol);
140     for (vector<string>::const_iterator iter = protobuf_framework_imports_.begin();
141          iter != protobuf_framework_imports_.end(); ++iter) {
142       printer->Print(
143           " #import <$framework_name$/$header$>\n",
144           "framework_name", framework_name,
145           "header", *iter);
146     }
147     printer->Print(
148         "#else\n");
149     for (vector<string>::const_iterator iter = protobuf_non_framework_imports_.begin();
150          iter != protobuf_non_framework_imports_.end(); ++iter) {
151       printer->Print(
152           " #import \"$header$\"\n",
153           "header", *iter);
154     }
155     printer->Print(
156         "#endif\n");
157 
158     add_blank_line = true;
159   }
160 
161   if (other_framework_imports_.size() > 0) {
162     if (add_blank_line) {
163       printer->Print("\n");
164     }
165 
166     for (vector<string>::const_iterator iter = other_framework_imports_.begin();
167          iter != other_framework_imports_.end(); ++iter) {
168       printer->Print(
169           " #import <$header$>\n",
170           "header", *iter);
171     }
172 
173     add_blank_line = true;
174   }
175 
176   if (other_imports_.size() > 0) {
177     if (add_blank_line) {
178       printer->Print("\n");
179     }
180 
181     for (vector<string>::const_iterator iter = other_imports_.begin();
182          iter != other_imports_.end(); ++iter) {
183       printer->Print(
184           " #import \"$header$\"\n",
185           "header", *iter);
186     }
187   }
188 }
189 
ParseFrameworkMappings()190 void ImportWriter::ParseFrameworkMappings() {
191   need_to_parse_mapping_file_ = false;
192   if (options_.named_framework_to_proto_path_mappings_path.empty()) {
193     return;  // Nothing to do.
194   }
195 
196   ProtoFrameworkCollector collector(&proto_file_to_framework_name_);
197   string parse_error;
198   if (!ParseSimpleFile(options_.named_framework_to_proto_path_mappings_path,
199                        &collector, &parse_error)) {
200     cerr << "error parsing " << options_.named_framework_to_proto_path_mappings_path
201          << " : " << parse_error << endl;
202     cerr.flush();
203   }
204 }
205 
ConsumeLine(const StringPiece & line,string * out_error)206 bool ImportWriter::ProtoFrameworkCollector::ConsumeLine(
207     const StringPiece& line, string* out_error) {
208   int offset = line.find(':');
209   if (offset == StringPiece::npos) {
210     *out_error =
211         string("Framework/proto file mapping line without colon sign: '") +
212         line.ToString() + "'.";
213     return false;
214   }
215   StringPiece framework_name(line, 0, offset);
216   StringPiece proto_file_list(line, offset + 1, line.length() - offset - 1);
217   StringPieceTrimWhitespace(&framework_name);
218 
219   int start = 0;
220   while (start < proto_file_list.length()) {
221     offset = proto_file_list.find(',', start);
222     if (offset == StringPiece::npos) {
223       offset = proto_file_list.length();
224     }
225 
226     StringPiece proto_file(proto_file_list, start, offset - start);
227     StringPieceTrimWhitespace(&proto_file);
228     if (proto_file.size() != 0) {
229       map<string, string>::iterator existing_entry =
230           map_->find(proto_file.ToString());
231       if (existing_entry != map_->end()) {
232         cerr << "warning: duplicate proto file reference, replacing framework entry for '"
233              << proto_file.ToString() << "' with '" << framework_name.ToString()
234              << "' (was '" << existing_entry->second << "')." << endl;
235         cerr.flush();
236       }
237 
238       if (proto_file.find(' ') != StringPiece::npos) {
239         cerr << "note: framework mapping file had a proto file with a space in, hopefully that isn't a missing comma: '"
240              << proto_file.ToString() << "'" << endl;
241         cerr.flush();
242       }
243 
244       (*map_)[proto_file.ToString()] = framework_name.ToString();
245     }
246 
247     start = offset + 1;
248   }
249 
250   return true;
251 }
252 
253 }  // namespace
254 
255 
FileGenerator(const FileDescriptor * file,const Options & options)256 FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
257     : file_(file),
258       root_class_name_(FileClassName(file)),
259       is_public_dep_(false),
260       options_(options) {
261   for (int i = 0; i < file_->enum_type_count(); i++) {
262     EnumGenerator *generator = new EnumGenerator(file_->enum_type(i));
263     enum_generators_.push_back(generator);
264   }
265   for (int i = 0; i < file_->message_type_count(); i++) {
266     MessageGenerator *generator =
267         new MessageGenerator(root_class_name_, file_->message_type(i), options_);
268     message_generators_.push_back(generator);
269   }
270   for (int i = 0; i < file_->extension_count(); i++) {
271     ExtensionGenerator *generator =
272         new ExtensionGenerator(root_class_name_, file_->extension(i));
273     extension_generators_.push_back(generator);
274   }
275 }
276 
~FileGenerator()277 FileGenerator::~FileGenerator() {
278   STLDeleteContainerPointers(dependency_generators_.begin(),
279                              dependency_generators_.end());
280   STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end());
281   STLDeleteContainerPointers(message_generators_.begin(),
282                              message_generators_.end());
283   STLDeleteContainerPointers(extension_generators_.begin(),
284                              extension_generators_.end());
285 }
286 
GenerateHeader(io::Printer * printer)287 void FileGenerator::GenerateHeader(io::Printer *printer) {
288   PrintFileRuntimePreamble(printer, "GPBProtocolBuffers.h");
289 
290   // Add some verification that the generated code matches the source the
291   // code is being compiled with.
292   printer->Print(
293       "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n"
294       "#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.\n"
295       "#endif\n"
296       "\n",
297       "protoc_gen_objc_version",
298       SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION));
299 
300   // #import any headers for "public imports" in the proto file.
301   {
302     ImportWriter import_writer(options_);
303     const vector<FileGenerator *> &dependency_generators = DependencyGenerators();
304     for (vector<FileGenerator *>::const_iterator iter =
305              dependency_generators.begin();
306          iter != dependency_generators.end(); ++iter) {
307       if ((*iter)->IsPublicDependency()) {
308         import_writer.AddFile(*iter);
309       }
310     }
311     import_writer.Print(printer);
312   }
313 
314   // Note:
315   //  deprecated-declarations suppression is only needed if some place in this
316   //    proto file is something deprecated or if it references something from
317   //    another file that is deprecated.
318   printer->Print(
319       "// @@protoc_insertion_point(imports)\n"
320       "\n"
321       "#pragma clang diagnostic push\n"
322       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
323       "\n"
324       "CF_EXTERN_C_BEGIN\n"
325       "\n");
326 
327   set<string> fwd_decls;
328   for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
329        iter != message_generators_.end(); ++iter) {
330     (*iter)->DetermineForwardDeclarations(&fwd_decls);
331   }
332   for (set<string>::const_iterator i(fwd_decls.begin());
333        i != fwd_decls.end(); ++i) {
334     printer->Print("$value$;\n", "value", *i);
335   }
336   if (fwd_decls.begin() != fwd_decls.end()) {
337     printer->Print("\n");
338   }
339 
340   printer->Print(
341       "NS_ASSUME_NONNULL_BEGIN\n"
342       "\n");
343 
344   // need to write out all enums first
345   for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
346        iter != enum_generators_.end(); ++iter) {
347     (*iter)->GenerateHeader(printer);
348   }
349 
350   for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
351        iter != message_generators_.end(); ++iter) {
352     (*iter)->GenerateEnumHeader(printer);
353   }
354 
355   // For extensions to chain together, the Root gets created even if there
356   // are no extensions.
357   printer->Print(
358       "#pragma mark - $root_class_name$\n"
359       "\n"
360       "/// Exposes the extension registry for this file.\n"
361       "///\n"
362       "/// The base class provides:\n"
363       "/// @code\n"
364       "///   + (GPBExtensionRegistry *)extensionRegistry;\n"
365       "/// @endcode\n"
366       "/// which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
367       "/// this file and all files that it depends on.\n"
368       "@interface $root_class_name$ : GPBRootObject\n"
369       "@end\n"
370       "\n",
371       "root_class_name", root_class_name_);
372 
373   if (extension_generators_.size() > 0) {
374     // The dynamic methods block is only needed if there are extensions.
375     printer->Print(
376         "@interface $root_class_name$ (DynamicMethods)\n",
377         "root_class_name", root_class_name_);
378 
379     for (vector<ExtensionGenerator *>::iterator iter =
380              extension_generators_.begin();
381          iter != extension_generators_.end(); ++iter) {
382       (*iter)->GenerateMembersHeader(printer);
383     }
384 
385     printer->Print("@end\n\n");
386   }  // extension_generators_.size() > 0
387 
388   for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
389        iter != message_generators_.end(); ++iter) {
390     (*iter)->GenerateMessageHeader(printer);
391   }
392 
393   printer->Print(
394       "NS_ASSUME_NONNULL_END\n"
395       "\n"
396       "CF_EXTERN_C_END\n"
397       "\n"
398       "#pragma clang diagnostic pop\n"
399       "\n"
400       "// @@protoc_insertion_point(global_scope)\n");
401 }
402 
GenerateSource(io::Printer * printer)403 void FileGenerator::GenerateSource(io::Printer *printer) {
404   // #import the runtime support.
405   PrintFileRuntimePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h");
406 
407   {
408     ImportWriter import_writer(options_);
409 
410     // #import the header for this proto file.
411     import_writer.AddFile(this);
412 
413     // #import the headers for anything that a plain dependency of this proto
414     // file (that means they were just an include, not a "public" include).
415     const vector<FileGenerator *> &dependency_generators =
416         DependencyGenerators();
417     for (vector<FileGenerator *>::const_iterator iter =
418              dependency_generators.begin();
419          iter != dependency_generators.end(); ++iter) {
420       if (!(*iter)->IsPublicDependency()) {
421         import_writer.AddFile(*iter);
422       }
423     }
424 
425     import_writer.Print(printer);
426   }
427 
428   bool includes_oneof = false;
429   for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
430        iter != message_generators_.end(); ++iter) {
431     if ((*iter)->IncludesOneOfDefinition()) {
432       includes_oneof = true;
433       break;
434     }
435   }
436 
437   // Note:
438   //  deprecated-declarations suppression is only needed if some place in this
439   //    proto file is something deprecated or if it references something from
440   //    another file that is deprecated.
441   printer->Print(
442       "// @@protoc_insertion_point(imports)\n"
443       "\n"
444       "#pragma clang diagnostic push\n"
445       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
446   if (includes_oneof) {
447     // The generated code for oneof's uses direct ivar access, suppress the
448     // warning incase developer turn that on in the context they compile the
449     // generated code.
450     printer->Print(
451         "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
452   }
453 
454   printer->Print(
455       "\n"
456       "#pragma mark - $root_class_name$\n"
457       "\n"
458       "@implementation $root_class_name$\n\n",
459       "root_class_name", root_class_name_);
460 
461   // Generate the extension initialization structures for the top level and
462   // any nested messages.
463   ostringstream extensions_stringstream;
464   if (file_->extension_count() + file_->message_type_count() > 0) {
465     io::OstreamOutputStream extensions_outputstream(&extensions_stringstream);
466     io::Printer extensions_printer(&extensions_outputstream, '$');
467     for (vector<ExtensionGenerator *>::iterator iter =
468              extension_generators_.begin();
469          iter != extension_generators_.end(); ++iter) {
470       (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
471     }
472     for (vector<MessageGenerator *>::iterator iter =
473              message_generators_.begin();
474          iter != message_generators_.end(); ++iter) {
475       (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
476     }
477     extensions_stringstream.flush();
478   }
479 
480   // If there were any extensions or this file has any dependencies, output
481   // a registry to override to create the file specific registry.
482   const string& extensions_str = extensions_stringstream.str();
483   if (extensions_str.length() > 0 || file_->dependency_count() > 0) {
484     printer->Print(
485         "+ (GPBExtensionRegistry*)extensionRegistry {\n"
486         "  // This is called by +initialize so there is no need to worry\n"
487         "  // about thread safety and initialization of registry.\n"
488         "  static GPBExtensionRegistry* registry = nil;\n"
489         "  if (!registry) {\n"
490         "    GPBDebugCheckRuntimeVersion();\n"
491         "    registry = [[GPBExtensionRegistry alloc] init];\n");
492 
493     printer->Indent();
494     printer->Indent();
495 
496     if (extensions_str.length() > 0) {
497       printer->Print(
498           "static GPBExtensionDescription descriptions[] = {\n");
499       printer->Indent();
500       printer->Print(extensions_str.c_str());
501       printer->Outdent();
502       printer->Print(
503           "};\n"
504           "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
505           "  GPBExtensionDescriptor *extension =\n"
506           "      [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]];\n"
507           "  [registry addExtension:extension];\n"
508           "  [self globallyRegisterExtension:extension];\n"
509           "  [extension release];\n"
510           "}\n");
511     }
512 
513     const vector<FileGenerator *> &dependency_generators =
514         DependencyGenerators();
515     for (vector<FileGenerator *>::const_iterator iter =
516              dependency_generators.begin();
517          iter != dependency_generators.end(); ++iter) {
518       printer->Print(
519           "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
520           "dependency", (*iter)->RootClassName());
521     }
522 
523     printer->Outdent();
524     printer->Outdent();
525 
526     printer->Print(
527         "  }\n"
528         "  return registry;\n"
529         "}\n"
530         "\n");
531   }
532 
533   printer->Print("@end\n\n");
534 
535   // File descriptor only needed if there are messages to use it.
536   if (message_generators_.size() > 0) {
537     string syntax;
538     switch (file_->syntax()) {
539       case FileDescriptor::SYNTAX_UNKNOWN:
540         syntax = "GPBFileSyntaxUnknown";
541         break;
542       case FileDescriptor::SYNTAX_PROTO2:
543         syntax = "GPBFileSyntaxProto2";
544         break;
545       case FileDescriptor::SYNTAX_PROTO3:
546         syntax = "GPBFileSyntaxProto3";
547         break;
548     }
549     printer->Print(
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         "    GPBDebugCheckRuntimeVersion();\n"
558         "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
559         "                                                     syntax:$syntax$];\n"
560         "  }\n"
561         "  return descriptor;\n"
562         "}\n"
563         "\n",
564         "root_class_name", root_class_name_,
565         "package", file_->package(),
566         "syntax", syntax);
567   }
568 
569   for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
570        iter != enum_generators_.end(); ++iter) {
571     (*iter)->GenerateSource(printer);
572   }
573   for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
574        iter != message_generators_.end(); ++iter) {
575     (*iter)->GenerateSource(printer);
576   }
577 
578   printer->Print(
579     "\n"
580     "#pragma clang diagnostic pop\n"
581     "\n"
582     "// @@protoc_insertion_point(global_scope)\n");
583 }
584 
DependencyGenerators()585 const vector<FileGenerator *> &FileGenerator::DependencyGenerators() {
586   if (file_->dependency_count() != dependency_generators_.size()) {
587     set<string> public_import_names;
588     for (int i = 0; i < file_->public_dependency_count(); i++) {
589       public_import_names.insert(file_->public_dependency(i)->name());
590     }
591     for (int i = 0; i < file_->dependency_count(); i++) {
592       FileGenerator *generator =
593           new FileGenerator(file_->dependency(i), options_);
594       const string& name = file_->dependency(i)->name();
595       bool public_import = (public_import_names.count(name) != 0);
596       generator->SetIsPublicDependency(public_import);
597       dependency_generators_.push_back(generator);
598     }
599   }
600   return dependency_generators_;
601 }
602 
603 // Helper to print the import of the runtime support at the top of generated
604 // files. This currently only supports the runtime coming from a framework
605 // as defined by the official CocoaPod.
PrintFileRuntimePreamble(io::Printer * printer,const string & header_to_import) const606 void FileGenerator::PrintFileRuntimePreamble(
607     io::Printer* printer, const string& header_to_import) const {
608   printer->Print(
609       "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
610       "// source: $filename$\n"
611       "\n",
612       "filename", file_->name());
613 
614   const string framework_name(ProtobufLibraryFrameworkName);
615   const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
616   printer->Print(
617       "// This CPP symbol can be defined to use imports that match up to the framework\n"
618       "// imports needed when using CocoaPods.\n"
619       "#if !defined($cpp_symbol$)\n"
620       " #define $cpp_symbol$ 0\n"
621       "#endif\n"
622       "\n"
623       "#if $cpp_symbol$\n"
624       " #import <$framework_name$/$header$>\n"
625       "#else\n"
626       " #import \"$header$\"\n"
627       "#endif\n"
628       "\n",
629       "cpp_symbol", cpp_symbol,
630       "header", header_to_import,
631       "framework_name", framework_name);
632 }
633 
634 }  // namespace objectivec
635 }  // namespace compiler
636 }  // namespace protobuf
637 }  // namespace google
638