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
48namespace google {
49namespace protobuf {
50namespace compiler {
51namespace objectivec {
52
53namespace {
54
55// This is also found in GPBBootstrap.h, and needs to be kept in sync.
56const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30004;
57
58const char* kHeaderExtension = ".pbobjc.h";
59
60std::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).
66bool 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: message->nested_type(index: 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).
80bool 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: message->nested_type(index: 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).
94bool 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(message: file->message_type(index: 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).
108bool 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(message: file->message_type(index: i))) {
114 return true;
115 }
116 }
117 return false;
118}
119
120bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
121 for (int i = 0; i < file->dependency_count(); i++) {
122 if (dep == file->dependency(index: i)) {
123 return true;
124 }
125 }
126 return false;
127}
128
129struct FileDescriptorsOrderedByName {
130 inline bool operator()(const FileDescriptor* a,
131 const FileDescriptor* b) const {
132 return a->name() < b->name();
133 }
134};
135
136} // namespace
137
138FileGenerator::CommonState::CommonState() { }
139
140const FileGenerator::CommonState::MinDepsEntry&
141FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensionsInternal(
142 const FileDescriptor* file) {
143 auto it = deps_info_cache_.find(x: file);
144 if (it != deps_info_cache_.end()) {
145 return it->second;
146 }
147
148 std::set<const FileDescriptor*> min_deps_collector;
149 std::set<const FileDescriptor*> covered_deps_collector;
150 std::set<const FileDescriptor*> to_prune;
151 for (int i = 0; i < file->dependency_count(); i++) {
152 const FileDescriptor* dep = file->dependency(index: i);
153 MinDepsEntry dep_info =
154 CollectMinimalFileDepsContainingExtensionsInternal(file: dep);
155
156 // Everything the dep covered, this file will also cover.
157 covered_deps_collector.insert(first: dep_info.covered_deps.begin(), last: dep_info.covered_deps.end());
158 // Prune everything from the dep's covered list in case another dep lists it
159 // as a min dep.
160 to_prune.insert(first: dep_info.covered_deps.begin(), last: dep_info.covered_deps.end());
161
162 // Does the dep have any extensions...
163 if (dep_info.has_extensions) {
164 // Yes -> Add this file, prune its min_deps and add them to the covered deps.
165 min_deps_collector.insert(x: dep);
166 to_prune.insert(first: dep_info.min_deps.begin(), last: dep_info.min_deps.end());
167 covered_deps_collector.insert(first: dep_info.min_deps.begin(), last: dep_info.min_deps.end());
168 } else {
169 // No -> Just use its min_deps.
170 min_deps_collector.insert(first: dep_info.min_deps.begin(), last: dep_info.min_deps.end());
171 }
172 }
173
174 const bool file_has_exts = FileContainsExtensions(file);
175
176 // Fast path: if nothing to prune or there was only one dep, the prune work is
177 // a waste, skip it.
178 if (to_prune.empty() || file->dependency_count() == 1) {
179 return deps_info_cache_.insert(
180 x: {file, {.has_extensions: file_has_exts, .min_deps: min_deps_collector, .covered_deps: covered_deps_collector}}).first->second;
181 }
182
183 std::set<const FileDescriptor*> min_deps;
184 std::copy_if(first: min_deps_collector.begin(), last: min_deps_collector.end(),
185 result: std::inserter(x&: min_deps, i: min_deps.end()),
186 pred: [&](const FileDescriptor* value){
187 return to_prune.find(x: value) == to_prune.end();
188 });
189 return deps_info_cache_.insert(
190 x: {file, {.has_extensions: file_has_exts, .min_deps: min_deps, .covered_deps: covered_deps_collector}}).first->second;
191}
192
193// Collect the deps of the given file that contain extensions. This can be used to
194// create the chain of roots that need to be wired together.
195//
196// NOTE: If any changes are made to this and the supporting functions, you will
197// need to manually validate what the generated code is for the test files:
198// objectivec/Tests/unittest_extension_chain_*.proto
199// There are comments about what the expected code should be line and limited
200// testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
201// specifically).
202const std::vector<const FileDescriptor*>
203FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensions(
204 const FileDescriptor* file) {
205 std::set<const FileDescriptor*> min_deps =
206 CollectMinimalFileDepsContainingExtensionsInternal(file).min_deps;
207 // Sort the list since pointer order isn't stable across runs.
208 std::vector<const FileDescriptor*> result(min_deps.begin(), min_deps.end());
209 std::sort(first: result.begin(), last: result.end(), comp: FileDescriptorsOrderedByName());
210 return result;
211}
212
213FileGenerator::FileGenerator(const FileDescriptor* file,
214 const GenerationOptions& generation_options,
215 CommonState& common_state)
216 : file_(file),
217 generation_options_(generation_options),
218 common_state_(common_state),
219 root_class_name_(FileClassName(file)),
220 is_bundled_proto_(IsProtobufLibraryBundledProtoFile(file)) {
221 for (int i = 0; i < file_->enum_type_count(); i++) {
222 EnumGenerator* generator = new EnumGenerator(file_->enum_type(index: i));
223 enum_generators_.emplace_back(args&: generator);
224 }
225 for (int i = 0; i < file_->message_type_count(); i++) {
226 MessageGenerator* generator =
227 new MessageGenerator(root_class_name_, file_->message_type(index: i));
228 message_generators_.emplace_back(args&: generator);
229 }
230 for (int i = 0; i < file_->extension_count(); i++) {
231 ExtensionGenerator* generator =
232 new ExtensionGenerator(root_class_name_, file_->extension(index: i));
233 extension_generators_.emplace_back(args&: generator);
234 }
235}
236
237FileGenerator::~FileGenerator() {}
238
239void FileGenerator::GenerateHeader(io::Printer* printer) {
240 std::vector<std::string> headers;
241 // Generated files bundled with the library get minimal imports, everything
242 // else gets the wrapper so everything is usable.
243 if (is_bundled_proto_) {
244 headers.push_back(x: "GPBDescriptor.h");
245 headers.push_back(x: "GPBMessage.h");
246 headers.push_back(x: "GPBRootObject.h");
247 for (int i = 0; i < file_->dependency_count(); i++) {
248 const std::string header_name = BundledFileName(file: file_->dependency(index: i));
249 headers.push_back(x: header_name);
250 }
251 } else {
252 headers.push_back(x: "GPBProtocolBuffers.h");
253 }
254 PrintFileRuntimePreamble(printer, headers_to_import: headers);
255
256 // Add some verification that the generated code matches the source the
257 // code is being compiled with.
258 // NOTE: This captures the raw numeric values at the time the generator was
259 // compiled, since that will be the versions for the ObjC runtime at that
260 // time. The constants in the generated code will then get their values at
261 // at compile time (so checking against the headers being used to compile).
262 printer->Print(
263 text: "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n"
264 "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n"
265 "#endif\n"
266 "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n"
267 "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n"
268 "#endif\n"
269 "\n",
270 args: "google_protobuf_objc_version", args: StrCat(a: GOOGLE_PROTOBUF_OBJC_VERSION));
271
272 // The bundled protos (WKTs) don't use of forward declarations.
273 bool headers_use_forward_declarations =
274 generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
275
276 {
277 ImportWriter import_writer(
278 generation_options_.generate_for_named_framework,
279 generation_options_.named_framework_to_proto_path_mappings_path,
280 generation_options_.runtime_import_prefix,
281 /* include_wkt_imports = */ false);
282 const std::string header_extension(kHeaderExtension);
283 if (headers_use_forward_declarations) {
284 // #import any headers for "public imports" in the proto file.
285 for (int i = 0; i < file_->public_dependency_count(); i++) {
286 import_writer.AddFile(file: file_->public_dependency(index: i), header_extension);
287 }
288 } else {
289 for (int i = 0; i < file_->dependency_count(); i++) {
290 import_writer.AddFile(file: file_->dependency(index: i), header_extension);
291 }
292 }
293 import_writer.Print(printer);
294 }
295
296 // Note:
297 // deprecated-declarations suppression is only needed if some place in this
298 // proto file is something deprecated or if it references something from
299 // another file that is deprecated.
300 printer->Print(
301 text: "// @@protoc_insertion_point(imports)\n"
302 "\n"
303 "#pragma clang diagnostic push\n"
304 "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
305 "\n"
306 "CF_EXTERN_C_BEGIN\n"
307 "\n");
308
309 std::set<std::string> fwd_decls;
310 for (const auto& generator : message_generators_) {
311 generator->DetermineForwardDeclarations(
312 fwd_decls: &fwd_decls,
313 /* include_external_types = */ headers_use_forward_declarations);
314 }
315 for (std::set<std::string>::const_iterator i(fwd_decls.begin());
316 i != fwd_decls.end(); ++i) {
317 printer->Print(text: "$value$;\n", args: "value", args: *i);
318 }
319 if (fwd_decls.begin() != fwd_decls.end()) {
320 printer->Print(text: "\n");
321 }
322
323 printer->Print(
324 text: "NS_ASSUME_NONNULL_BEGIN\n"
325 "\n");
326
327 // need to write out all enums first
328 for (const auto& generator : enum_generators_) {
329 generator->GenerateHeader(printer);
330 }
331
332 for (const auto& generator : message_generators_) {
333 generator->GenerateEnumHeader(printer);
334 }
335
336 // For extensions to chain together, the Root gets created even if there
337 // are no extensions.
338 printer->Print(
339 text: "#pragma mark - $root_class_name$\n"
340 "\n"
341 "/**\n"
342 " * Exposes the extension registry for this file.\n"
343 " *\n"
344 " * The base class provides:\n"
345 " * @code\n"
346 " * + (GPBExtensionRegistry *)extensionRegistry;\n"
347 " * @endcode\n"
348 " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
349 " * this file and all files that it depends on.\n"
350 " **/\n"
351 "GPB_FINAL @interface $root_class_name$ : GPBRootObject\n"
352 "@end\n"
353 "\n",
354 args: "root_class_name", args: root_class_name_);
355
356 if (!extension_generators_.empty()) {
357 // The dynamic methods block is only needed if there are extensions.
358 printer->Print(
359 text: "@interface $root_class_name$ (DynamicMethods)\n",
360 args: "root_class_name", args: root_class_name_);
361
362 for (const auto& generator : extension_generators_) {
363 generator->GenerateMembersHeader(printer);
364 }
365
366 printer->Print(text: "@end\n\n");
367 } // !extension_generators_.empty()
368
369 for (const auto& generator : message_generators_) {
370 generator->GenerateMessageHeader(printer);
371 }
372
373 printer->Print(
374 text: "NS_ASSUME_NONNULL_END\n"
375 "\n"
376 "CF_EXTERN_C_END\n"
377 "\n"
378 "#pragma clang diagnostic pop\n"
379 "\n"
380 "// @@protoc_insertion_point(global_scope)\n");
381}
382
383void FileGenerator::GenerateSource(io::Printer* printer) {
384 // #import the runtime support.
385 std::vector<std::string> headers;
386 headers.push_back(x: "GPBProtocolBuffers_RuntimeSupport.h");
387 if (is_bundled_proto_) {
388 headers.push_back(x: BundledFileName(file: file_));
389 }
390 PrintFileRuntimePreamble(printer, headers_to_import: headers);
391
392 // Enums use atomic in the generated code, so add the system import as needed.
393 if (FileContainsEnums(file: file_)) {
394 printer->Print(
395 text: "#import <stdatomic.h>\n"
396 "\n");
397 }
398
399 std::vector<const FileDescriptor*> deps_with_extensions =
400 common_state_.CollectMinimalFileDepsContainingExtensions(file: file_);
401
402 // The bundled protos (WKTs) don't use of forward declarations.
403 bool headers_use_forward_declarations =
404 generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
405
406 {
407 ImportWriter import_writer(
408 generation_options_.generate_for_named_framework,
409 generation_options_.named_framework_to_proto_path_mappings_path,
410 generation_options_.runtime_import_prefix,
411 /* include_wkt_imports = */ false);
412 const std::string header_extension(kHeaderExtension);
413
414 // #import the header for this proto file.
415 import_writer.AddFile(file: file_, header_extension);
416
417 if (headers_use_forward_declarations) {
418 // #import the headers for anything that a plain dependency of this proto
419 // file (that means they were just an include, not a "public" include).
420 std::set<std::string> public_import_names;
421 for (int i = 0; i < file_->public_dependency_count(); i++) {
422 public_import_names.insert(x: file_->public_dependency(index: i)->name());
423 }
424 for (int i = 0; i < file_->dependency_count(); i++) {
425 const FileDescriptor *dep = file_->dependency(index: i);
426 bool public_import = (public_import_names.count(x: dep->name()) != 0);
427 if (!public_import) {
428 import_writer.AddFile(file: dep, header_extension);
429 }
430 }
431 }
432
433 // If any indirect dependency provided extensions, it needs to be directly
434 // imported so it can get merged into the root's extensions registry.
435 // See the Note by CollectMinimalFileDepsContainingExtensions before
436 // changing this.
437 for (std::vector<const FileDescriptor*>::iterator iter =
438 deps_with_extensions.begin();
439 iter != deps_with_extensions.end(); ++iter) {
440 if (!IsDirectDependency(dep: *iter, file: file_)) {
441 import_writer.AddFile(file: *iter, header_extension);
442 }
443 }
444
445 import_writer.Print(printer);
446 }
447
448 bool includes_oneof = false;
449 for (const auto& generator : message_generators_) {
450 if (generator->IncludesOneOfDefinition()) {
451 includes_oneof = true;
452 break;
453 }
454 }
455
456 std::set<std::string> fwd_decls;
457 for (const auto& generator : message_generators_) {
458 generator->DetermineObjectiveCClassDefinitions(fwd_decls: &fwd_decls);
459 }
460 for (const auto& generator : extension_generators_) {
461 generator->DetermineObjectiveCClassDefinitions(fwd_decls: &fwd_decls);
462 }
463
464 // Note:
465 // deprecated-declarations suppression is only needed if some place in this
466 // proto file is something deprecated or if it references something from
467 // another file that is deprecated.
468 // dollar-in-identifier-extension is needed because we use references to
469 // objc class names that have $ in identifiers.
470 printer->Print(
471 text: "// @@protoc_insertion_point(imports)\n"
472 "\n"
473 "#pragma clang diagnostic push\n"
474 "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
475 if (includes_oneof) {
476 // The generated code for oneof's uses direct ivar access, suppress the
477 // warning in case developer turn that on in the context they compile the
478 // generated code.
479 printer->Print(
480 text: "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
481 }
482 if (!fwd_decls.empty()) {
483 printer->Print(
484 text: "#pragma clang diagnostic ignored \"-Wdollar-in-identifier-extension\"\n");
485 }
486 printer->Print(
487 text: "\n");
488 if (!fwd_decls.empty()) {
489 printer->Print(
490 text: "#pragma mark - Objective C Class declarations\n"
491 "// Forward declarations of Objective C classes that we can use as\n"
492 "// static values in struct initializers.\n"
493 "// We don't use [Foo class] because it is not a static value.\n");
494 }
495 for (const auto& i : fwd_decls) {
496 printer->Print(text: "$value$\n", args: "value", args: i);
497 }
498 if (!fwd_decls.empty()) {
499 printer->Print(text: "\n");
500 }
501 printer->Print(
502 text: "#pragma mark - $root_class_name$\n"
503 "\n"
504 "@implementation $root_class_name$\n\n",
505 args: "root_class_name", args: root_class_name_);
506
507 const bool file_contains_extensions = FileContainsExtensions(file: file_);
508
509 // If there were any extensions or this file has any dependencies, output
510 // a registry to override to create the file specific registry.
511 if (file_contains_extensions || !deps_with_extensions.empty()) {
512 printer->Print(
513 text: "+ (GPBExtensionRegistry*)extensionRegistry {\n"
514 " // This is called by +initialize so there is no need to worry\n"
515 " // about thread safety and initialization of registry.\n"
516 " static GPBExtensionRegistry* registry = nil;\n"
517 " if (!registry) {\n"
518 " GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
519 " registry = [[GPBExtensionRegistry alloc] init];\n");
520
521 printer->Indent();
522 printer->Indent();
523
524 if (file_contains_extensions) {
525 printer->Print(
526 text: "static GPBExtensionDescription descriptions[] = {\n");
527 printer->Indent();
528 for (const auto& generator : extension_generators_) {
529 generator->GenerateStaticVariablesInitialization(printer);
530 }
531 for (const auto& generator : message_generators_) {
532 generator->GenerateStaticVariablesInitialization(printer);
533 }
534 printer->Outdent();
535 printer->Print(
536 text: "};\n"
537 "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
538 " GPBExtensionDescriptor *extension =\n"
539 " [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]\n"
540 " usesClassRefs:YES];\n"
541 " [registry addExtension:extension];\n"
542 " [self globallyRegisterExtension:extension];\n"
543 " [extension release];\n"
544 "}\n");
545 }
546
547 if (deps_with_extensions.empty()) {
548 printer->Print(
549 text: "// None of the imports (direct or indirect) defined extensions, so no need to add\n"
550 "// them to this registry.\n");
551 } else {
552 printer->Print(
553 text: "// Merge in the imports (direct or indirect) that defined extensions.\n");
554 for (std::vector<const FileDescriptor*>::iterator iter =
555 deps_with_extensions.begin();
556 iter != deps_with_extensions.end(); ++iter) {
557 const std::string root_class_name(FileClassName(file: (*iter)));
558 printer->Print(
559 text: "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
560 args: "dependency", args: root_class_name);
561 }
562 }
563
564 printer->Outdent();
565 printer->Outdent();
566
567 printer->Print(
568 text: " }\n"
569 " return registry;\n"
570 "}\n");
571 } else {
572 if (file_->dependency_count() > 0) {
573 printer->Print(
574 text: "// No extensions in the file and none of the imports (direct or indirect)\n"
575 "// defined extensions, so no need to generate +extensionRegistry.\n");
576 } else {
577 printer->Print(
578 text: "// No extensions in the file and no imports, so no need to generate\n"
579 "// +extensionRegistry.\n");
580 }
581 }
582
583 printer->Print(text: "\n@end\n\n");
584
585 // File descriptor only needed if there are messages to use it.
586 if (!message_generators_.empty()) {
587 std::map<std::string, std::string> vars;
588 vars["root_class_name"] = root_class_name_;
589 vars["package"] = file_->package();
590 vars["objc_prefix"] = FileClassPrefix(file: file_);
591 switch (file_->syntax()) {
592 case FileDescriptor::SYNTAX_UNKNOWN:
593 vars["syntax"] = "GPBFileSyntaxUnknown";
594 break;
595 case FileDescriptor::SYNTAX_PROTO2:
596 vars["syntax"] = "GPBFileSyntaxProto2";
597 break;
598 case FileDescriptor::SYNTAX_PROTO3:
599 vars["syntax"] = "GPBFileSyntaxProto3";
600 break;
601 }
602 printer->Print(variables: vars,
603 text: "#pragma mark - $root_class_name$_FileDescriptor\n"
604 "\n"
605 "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
606 " // This is called by +initialize so there is no need to worry\n"
607 " // about thread safety of the singleton.\n"
608 " static GPBFileDescriptor *descriptor = NULL;\n"
609 " if (!descriptor) {\n"
610 " GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
611 if (!vars["objc_prefix"].empty()) {
612 printer->Print(
613 variables: vars,
614 text: " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
615 " objcPrefix:@\"$objc_prefix$\"\n"
616 " syntax:$syntax$];\n");
617 } else {
618 printer->Print(
619 variables: vars,
620 text: " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
621 " syntax:$syntax$];\n");
622 }
623 printer->Print(
624 text: " }\n"
625 " return descriptor;\n"
626 "}\n"
627 "\n");
628 }
629
630 for (const auto& generator : enum_generators_) {
631 generator->GenerateSource(printer);
632 }
633 for (const auto& generator : message_generators_) {
634 generator->GenerateSource(printer);
635 }
636
637 printer->Print(
638 text: "\n"
639 "#pragma clang diagnostic pop\n"
640 "\n"
641 "// @@protoc_insertion_point(global_scope)\n");
642}
643
644// Helper to print the import of the runtime support at the top of generated
645// files. This currently only supports the runtime coming from a framework
646// as defined by the official CocoaPod.
647void FileGenerator::PrintFileRuntimePreamble(
648 io::Printer* printer,
649 const std::vector<std::string>& headers_to_import) const {
650 printer->Print(
651 text: "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
652 "// source: $filename$\n"
653 "\n",
654 args: "filename", args: file_->name());
655
656 if (is_bundled_proto_) {
657 // This is basically a clone of ImportWriter::PrintRuntimeImports() but
658 // without the CPP symbol gate, since within the bundled files, that isn't
659 // needed.
660 std::string import_prefix = generation_options_.runtime_import_prefix;
661 if (!import_prefix.empty()) {
662 import_prefix += "/";
663 }
664 for (const auto& header : headers_to_import) {
665 printer->Print(
666 text: "#import \"$import_prefix$$header$\"\n",
667 args: "import_prefix", args: import_prefix,
668 args: "header", args: header);
669 }
670 } else {
671 ImportWriter::PrintRuntimeImports(
672 printer, header_to_import: headers_to_import, runtime_import_prefix: generation_options_.runtime_import_prefix, default_cpp_symbol: true);
673 }
674
675 printer->Print(text: "\n");
676}
677
678} // namespace objectivec
679} // namespace compiler
680} // namespace protobuf
681} // namespace google
682