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