|
@@ -45,6 +45,10 @@
|
|
|
|
|
|
namespace google {
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace protobuf {
|
|
|
|
+namespace compiler {
|
|
|
|
+namespace objectivec {
|
|
|
|
+
|
|
|
|
+namespace {
|
|
|
|
|
|
// This is also found in GPBBootstrap.h, and needs to be kept in sync. It
|
|
// This is also found in GPBBootstrap.h, and needs to be kept in sync. It
|
|
// is the version check done to ensure generated code works with the current
|
|
// is the version check done to ensure generated code works with the current
|
|
@@ -53,8 +57,106 @@ const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001;
|
|
|
|
|
|
const char* kHeaderExtension = ".pbobjc.h";
|
|
const char* kHeaderExtension = ".pbobjc.h";
|
|
|
|
|
|
-namespace compiler {
|
|
|
|
-namespace objectivec {
|
|
|
|
|
|
+// Checks if a message contains any extension definitions (on the message or
|
|
|
|
+// a nested message under it).
|
|
|
|
+bool MessageContainsExtensions(const Descriptor* message) {
|
|
|
|
+ if (message->extension_count() > 0) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < message->nested_type_count(); i++) {
|
|
|
|
+ if (MessageContainsExtensions(message->nested_type(i))) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Checks if the file contains any extensions definitions (at the root or
|
|
|
|
+// nested under a message).
|
|
|
|
+bool FileContainsExtensions(const FileDescriptor* file) {
|
|
|
|
+ if (file->extension_count() > 0) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < file->message_type_count(); i++) {
|
|
|
|
+ if (MessageContainsExtensions(file->message_type(i))) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Helper for CollectMinimalFileDepsContainingExtensionsWorker that marks all
|
|
|
|
+// deps as visited and prunes them from the needed files list.
|
|
|
|
+void PruneFileAndDepsMarkingAsVisited(
|
|
|
|
+ const FileDescriptor* file,
|
|
|
|
+ vector<const FileDescriptor*>* files,
|
|
|
|
+ set<const FileDescriptor*>* files_visited) {
|
|
|
|
+ vector<const FileDescriptor*>::iterator iter =
|
|
|
|
+ std::find(files->begin(), files->end(), file);
|
|
|
|
+ if (iter != files->end()) {
|
|
|
|
+ files->erase(iter);
|
|
|
|
+ }
|
|
|
|
+ files_visited->insert(file);
|
|
|
|
+ for (int i = 0; i < file->dependency_count(); i++) {
|
|
|
|
+ PruneFileAndDepsMarkingAsVisited(file->dependency(i), files, files_visited);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Helper for CollectMinimalFileDepsContainingExtensions.
|
|
|
|
+void CollectMinimalFileDepsContainingExtensionsWorker(
|
|
|
|
+ const FileDescriptor* file,
|
|
|
|
+ vector<const FileDescriptor*>* files,
|
|
|
|
+ set<const FileDescriptor*>* files_visited) {
|
|
|
|
+ if (files_visited->find(file) != files_visited->end()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ files_visited->insert(file);
|
|
|
|
+
|
|
|
|
+ if (FileContainsExtensions(file)) {
|
|
|
|
+ files->push_back(file);
|
|
|
|
+ for (int i = 0; i < file->dependency_count(); i++) {
|
|
|
|
+ const FileDescriptor* dep = file->dependency(i);
|
|
|
|
+ PruneFileAndDepsMarkingAsVisited(dep, files, files_visited);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ for (int i = 0; i < file->dependency_count(); i++) {
|
|
|
|
+ const FileDescriptor* dep = file->dependency(i);
|
|
|
|
+ CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
|
|
|
|
+ files_visited);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Collect the deps of the given file that contain extensions. This can be used to
|
|
|
|
+// create the chain of roots that need to be wired together.
|
|
|
|
+//
|
|
|
|
+// NOTE: If any changes are made to this and the supporting functions, you will
|
|
|
|
+// need to manually validate what the generated code is for the test files:
|
|
|
|
+// objectivec/Tests/unittest_extension_chain_*.proto
|
|
|
|
+// There are comments about what the expected code should be line and limited
|
|
|
|
+// testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
|
|
|
|
+// specifically).
|
|
|
|
+void CollectMinimalFileDepsContainingExtensions(
|
|
|
|
+ const FileDescriptor* file,
|
|
|
|
+ vector<const FileDescriptor*>* files) {
|
|
|
|
+ set<const FileDescriptor*> files_visited;
|
|
|
|
+ for (int i = 0; i < file->dependency_count(); i++) {
|
|
|
|
+ const FileDescriptor* dep = file->dependency(i);
|
|
|
|
+ CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
|
|
|
|
+ &files_visited);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
|
|
|
|
+ for (int i = 0; i < file->dependency_count(); i++) {
|
|
|
|
+ if (dep == file->dependency(i)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+} // namespace
|
|
|
|
|
|
FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
|
|
FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
|
|
: file_(file),
|
|
: file_(file),
|
|
@@ -204,6 +306,9 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
// #import the runtime support.
|
|
// #import the runtime support.
|
|
PrintFileRuntimePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h");
|
|
PrintFileRuntimePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h");
|
|
|
|
|
|
|
|
+ vector<const FileDescriptor*> deps_with_extensions;
|
|
|
|
+ CollectMinimalFileDepsContainingExtensions(file_, &deps_with_extensions);
|
|
|
|
+
|
|
{
|
|
{
|
|
ImportWriter import_writer(
|
|
ImportWriter import_writer(
|
|
options_.generate_for_named_framework,
|
|
options_.generate_for_named_framework,
|
|
@@ -227,6 +332,18 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // If any indirect dependency provided extensions, it needs to be directly
|
|
|
|
+ // imported so it can get merged into the root's extensions registry.
|
|
|
|
+ // See the Note by CollectMinimalFileDepsContainingExtensions before
|
|
|
|
+ // changing this.
|
|
|
|
+ for (vector<const FileDescriptor *>::iterator iter =
|
|
|
|
+ deps_with_extensions.begin();
|
|
|
|
+ iter != deps_with_extensions.end(); ++iter) {
|
|
|
|
+ if (!IsDirectDependency(*iter, file_)) {
|
|
|
|
+ import_writer.AddFile(*iter, header_extension);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
import_writer.Print(printer);
|
|
import_writer.Print(printer);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -263,29 +380,11 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
"@implementation $root_class_name$\n\n",
|
|
"@implementation $root_class_name$\n\n",
|
|
"root_class_name", root_class_name_);
|
|
"root_class_name", root_class_name_);
|
|
|
|
|
|
- // Generate the extension initialization structures for the top level and
|
|
|
|
- // any nested messages.
|
|
|
|
- ostringstream extensions_stringstream;
|
|
|
|
- if (file_->extension_count() + file_->message_type_count() > 0) {
|
|
|
|
- io::OstreamOutputStream extensions_outputstream(&extensions_stringstream);
|
|
|
|
- io::Printer extensions_printer(&extensions_outputstream, '$');
|
|
|
|
- for (vector<ExtensionGenerator *>::iterator iter =
|
|
|
|
- extension_generators_.begin();
|
|
|
|
- iter != extension_generators_.end(); ++iter) {
|
|
|
|
- (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
|
|
|
|
- }
|
|
|
|
- for (vector<MessageGenerator *>::iterator iter =
|
|
|
|
- message_generators_.begin();
|
|
|
|
- iter != message_generators_.end(); ++iter) {
|
|
|
|
- (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
|
|
|
|
- }
|
|
|
|
- extensions_stringstream.flush();
|
|
|
|
- }
|
|
|
|
|
|
+ const bool file_contains_extensions = FileContainsExtensions(file_);
|
|
|
|
|
|
// If there were any extensions or this file has any dependencies, output
|
|
// If there were any extensions or this file has any dependencies, output
|
|
// a registry to override to create the file specific registry.
|
|
// a registry to override to create the file specific registry.
|
|
- const string& extensions_str = extensions_stringstream.str();
|
|
|
|
- if (extensions_str.length() > 0 || file_->dependency_count() > 0) {
|
|
|
|
|
|
+ if (file_contains_extensions || !deps_with_extensions.empty()) {
|
|
printer->Print(
|
|
printer->Print(
|
|
"+ (GPBExtensionRegistry*)extensionRegistry {\n"
|
|
"+ (GPBExtensionRegistry*)extensionRegistry {\n"
|
|
" // This is called by +initialize so there is no need to worry\n"
|
|
" // This is called by +initialize so there is no need to worry\n"
|
|
@@ -298,11 +397,20 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
printer->Indent();
|
|
printer->Indent();
|
|
printer->Indent();
|
|
printer->Indent();
|
|
|
|
|
|
- if (extensions_str.length() > 0) {
|
|
|
|
|
|
+ if (file_contains_extensions) {
|
|
printer->Print(
|
|
printer->Print(
|
|
"static GPBExtensionDescription descriptions[] = {\n");
|
|
"static GPBExtensionDescription descriptions[] = {\n");
|
|
printer->Indent();
|
|
printer->Indent();
|
|
- printer->Print(extensions_str.c_str());
|
|
|
|
|
|
+ for (vector<ExtensionGenerator *>::iterator iter =
|
|
|
|
+ extension_generators_.begin();
|
|
|
|
+ iter != extension_generators_.end(); ++iter) {
|
|
|
|
+ (*iter)->GenerateStaticVariablesInitialization(printer);
|
|
|
|
+ }
|
|
|
|
+ for (vector<MessageGenerator *>::iterator iter =
|
|
|
|
+ message_generators_.begin();
|
|
|
|
+ iter != message_generators_.end(); ++iter) {
|
|
|
|
+ (*iter)->GenerateStaticVariablesInitialization(printer);
|
|
|
|
+ }
|
|
printer->Outdent();
|
|
printer->Outdent();
|
|
printer->Print(
|
|
printer->Print(
|
|
"};\n"
|
|
"};\n"
|
|
@@ -315,11 +423,21 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
"}\n");
|
|
"}\n");
|
|
}
|
|
}
|
|
|
|
|
|
- for (int i = 0; i < file_->dependency_count(); i++) {
|
|
|
|
- const string root_class_name(FileClassName(file_->dependency(i)));
|
|
|
|
|
|
+ if (deps_with_extensions.empty()) {
|
|
printer->Print(
|
|
printer->Print(
|
|
- "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
|
|
|
|
- "dependency", root_class_name);
|
|
|
|
|
|
+ "// None of the imports (direct or indirect) defined extensions, so no need to add\n"
|
|
|
|
+ "// them to this registry.\n");
|
|
|
|
+ } else {
|
|
|
|
+ printer->Print(
|
|
|
|
+ "// Merge in the imports (direct or indirect) that defined extensions.\n");
|
|
|
|
+ for (vector<const FileDescriptor *>::iterator iter =
|
|
|
|
+ deps_with_extensions.begin();
|
|
|
|
+ iter != deps_with_extensions.end(); ++iter) {
|
|
|
|
+ const string root_class_name(FileClassName((*iter)));
|
|
|
|
+ printer->Print(
|
|
|
|
+ "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
|
|
|
|
+ "dependency", root_class_name);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
printer->Outdent();
|
|
printer->Outdent();
|
|
@@ -328,11 +446,20 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
|
|
printer->Print(
|
|
printer->Print(
|
|
" }\n"
|
|
" }\n"
|
|
" return registry;\n"
|
|
" return registry;\n"
|
|
- "}\n"
|
|
|
|
- "\n");
|
|
|
|
|
|
+ "}\n");
|
|
|
|
+ } else {
|
|
|
|
+ if (file_->dependency_count() > 0) {
|
|
|
|
+ printer->Print(
|
|
|
|
+ "// No extensions in the file and none of the imports (direct or indirect)\n"
|
|
|
|
+ "// defined extensions, so no need to generate +extensionRegistry.\n");
|
|
|
|
+ } else {
|
|
|
|
+ printer->Print(
|
|
|
|
+ "// No extensions in the file and no imports, so no need to generate\n"
|
|
|
|
+ "// +extensionRegistry.\n");
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- printer->Print("@end\n\n");
|
|
|
|
|
|
+ printer->Print("\n@end\n\n");
|
|
|
|
|
|
// File descriptor only needed if there are messages to use it.
|
|
// File descriptor only needed if there are messages to use it.
|
|
if (message_generators_.size() > 0) {
|
|
if (message_generators_.size() > 0) {
|