1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | #include <google/protobuf/compiler/objectivec/objectivec_file.h> |
32 | #include <google/protobuf/compiler/objectivec/objectivec_enum.h> |
33 | #include <google/protobuf/compiler/objectivec/objectivec_extension.h> |
34 | #include <google/protobuf/compiler/objectivec/objectivec_helpers.h> |
35 | #include <google/protobuf/compiler/objectivec/objectivec_message.h> |
36 | #include <google/protobuf/compiler/code_generator.h> |
37 | #include <google/protobuf/io/printer.h> |
38 | #include <google/protobuf/io/zero_copy_stream_impl.h> |
39 | #include <google/protobuf/stubs/stl_util.h> |
40 | #include <google/protobuf/stubs/strutil.h> |
41 | #include <algorithm> // std::find() |
42 | #include <iostream> |
43 | #include <sstream> |
44 | |
45 | // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some |
46 | // error cases, so it seems to be ok to use as a back door for errors. |
47 | |
48 | namespace google { |
49 | namespace protobuf { |
50 | namespace compiler { |
51 | namespace objectivec { |
52 | |
53 | namespace { |
54 | |
55 | // This is also found in GPBBootstrap.h, and needs to be kept in sync. |
56 | const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30004; |
57 | |
58 | const char* = ".pbobjc.h" ; |
59 | |
60 | std::string BundledFileName(const FileDescriptor* file) { |
61 | return "GPB" + FilePathBasename(file) + kHeaderExtension; |
62 | } |
63 | |
64 | // Checks if a message contains any enums definitions (on the message or |
65 | // a nested message under it). |
66 | bool MessageContainsEnums(const Descriptor* message) { |
67 | if (message->enum_type_count() > 0) { |
68 | return true; |
69 | } |
70 | for (int i = 0; i < message->nested_type_count(); i++) { |
71 | if (MessageContainsEnums(message: 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). |
80 | bool MessageContainsExtensions(const Descriptor* message) { |
81 | if (message->extension_count() > 0) { |
82 | return true; |
83 | } |
84 | for (int i = 0; i < message->nested_type_count(); i++) { |
85 | if (MessageContainsExtensions(message: 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). |
94 | bool FileContainsEnums(const FileDescriptor* file) { |
95 | if (file->enum_type_count() > 0) { |
96 | return true; |
97 | } |
98 | for (int i = 0; i < file->message_type_count(); i++) { |
99 | if (MessageContainsEnums(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). |
108 | bool FileContainsExtensions(const FileDescriptor* file) { |
109 | if (file->extension_count() > 0) { |
110 | return true; |
111 | } |
112 | for (int i = 0; i < file->message_type_count(); i++) { |
113 | if (MessageContainsExtensions(message: file->message_type(index: i))) { |
114 | return true; |
115 | } |
116 | } |
117 | return false; |
118 | } |
119 | |
120 | bool 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 | |
129 | struct 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 | |
138 | FileGenerator::CommonState::CommonState() { } |
139 | |
140 | const FileGenerator::CommonState::MinDepsEntry& |
141 | FileGenerator::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). |
202 | const std::vector<const FileDescriptor*> |
203 | FileGenerator::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 | |
213 | FileGenerator::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 | |
237 | FileGenerator::~FileGenerator() {} |
238 | |
239 | void FileGenerator::(io::Printer* printer) { |
240 | std::vector<std::string> ; |
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 = 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 = |
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 (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 | |
383 | void FileGenerator::GenerateSource(io::Printer* printer) { |
384 | // #import the runtime support. |
385 | std::vector<std::string> ; |
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 = |
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 (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. |
647 | void FileGenerator::PrintFileRuntimePreamble( |
648 | io::Printer* printer, |
649 | const std::vector<std::string>& ) 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& : 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 | |