diff --git a/src/google/protobuf/compiler/objectivec/file.cc b/src/google/protobuf/compiler/objectivec/file.cc index 2d7b5bb05c..3ba3f9af65 100644 --- a/src/google/protobuf/compiler/objectivec/file.cc +++ b/src/google/protobuf/compiler/objectivec/file.cc @@ -373,6 +373,92 @@ void FileGenerator::GenerateSource(io::Printer* p) const { }); } +void FileGenerator::GenerateGlobalSource(io::Printer* p) const { + std::vector deps_with_extensions = + common_state_->CollectMinimalFileDepsContainingExtensions(file_); + + // 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. + std::vector extra_files; + for (auto& dep : deps_with_extensions) { + if (!IsDirectDependency(dep, file_)) { + extra_files.push_back(dep); + } + } + + absl::btree_set fwd_decls; + for (const auto& generator : extension_generators_) { + generator->DetermineObjectiveCClassDefinitions(&fwd_decls); + } + + std::vector ignored_warnings; + if (!fwd_decls.empty()) { + ignored_warnings.push_back("dollar-in-identifier-extension"); + } + + GenerateFile( + p, GeneratedFileType::kSource, ignored_warnings, extra_files, [&] { + if (!fwd_decls.empty()) { + p->Print( + // clang-format off + "#pragma mark - Objective C Class declarations\n" + "// Forward declarations of Objective C classes that we can use as\n" + "// static values in struct initializers.\n" + "// We don't use [Foo class] because it is not a static value.\n" + "$fwd_decls$\n" + "\n", + // clang-format on + "fwd_decls", absl::StrJoin(fwd_decls, "\n")); + } + + PrintRootImplementation(p, deps_with_extensions); + PrintFileDescriptorImplementation(p); + }); +} + +void FileGenerator::GenerateSourceForEnum(int idx, io::Printer* p) const { + GenerateFile(p, GeneratedFileType::kSource, + [&] { enum_generators_[idx]->GenerateSource(p); }); +} + +void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) const { + const auto& generator = message_generators_[idx]; + + absl::btree_set fwd_decls; + generator->DetermineObjectiveCClassDefinitions(&fwd_decls); + + std::vector ignored_warnings; + // The generated code for oneof's uses direct ivar access, suppress the + // warning in case developer turn that on in the context they compile the + // generated code. + if (generator->IncludesOneOfDefinition()) { + ignored_warnings.push_back("direct-ivar-access"); + } + + GenerateFile(p, GeneratedFileType::kSource, ignored_warnings, {}, [&] { + p->Print( + "extern GPBFileDescriptor *$root_class_name$_FileDescriptor(void);\n\n", + "root_class_name", root_class_name_); + + if (!fwd_decls.empty()) { + p->Print( + // clang-format off + "#pragma mark - Objective C Class declarations\n" + "// Forward declarations of Objective C classes that we can use as\n" + "// static values in struct initializers.\n" + "// We don't use [Foo class] because it is not a static value.\n" + "$fwd_decls$\n" + "\n", + // clang-format on + "fwd_decls", absl::StrJoin(fwd_decls, "\n")); + } + + generator->GenerateSource(p); + }); +} + void FileGenerator::GenerateFile( io::Printer* p, GeneratedFileType file_type, const std::vector& ignored_warnings, @@ -593,10 +679,10 @@ void FileGenerator::PrintRootExtensionRegistryImplementation( p->Outdent(); // clang-format off - p->Print( - " }\n" - " return registry;\n" - "}\n"); + p->Print( + " }\n" + " return registry;\n" + "}\n"); // clang-format on } @@ -629,12 +715,18 @@ void FileGenerator::PrintFileDescriptorImplementation(io::Printer* p) const { objc_prefix, "\"\n"); } + if (generation_options_.experimental_multi_source_generation) { + vars["file_desc_scope"] = ""; + } else { + vars["file_desc_scope"] = "static "; + } + // clang-format off p->Print( vars, "#pragma mark - $root_class_name$_FileDescriptor\n" "\n" - "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n" + "$file_desc_scope$GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n" " // This is called by +initialize so there is no need to worry\n" " // about thread safety of the singleton.\n" " static GPBFileDescriptor *descriptor = NULL;\n" diff --git a/src/google/protobuf/compiler/objectivec/file.h b/src/google/protobuf/compiler/objectivec/file.h index f6d3879c74..de4e504627 100644 --- a/src/google/protobuf/compiler/objectivec/file.h +++ b/src/google/protobuf/compiler/objectivec/file.h @@ -84,6 +84,14 @@ class FileGenerator { void GenerateHeader(io::Printer* p) const; void GenerateSource(io::Printer* p) const; + int NumEnums() const { return enum_generators_.size(); } + int NumMessages() const { return message_generators_.size(); } + + void GenerateGlobalSource(io::Printer* p) const; + void GenerateSourceForMessage(int idx, io::Printer* p) const; + void GenerateSourceForEnum(int idx, io::Printer* p) const; + + private: enum class GeneratedFileType : int { kHeader, kSource }; void GenerateFile(io::Printer* p, GeneratedFileType file_type, @@ -109,7 +117,6 @@ class FileGenerator { generation_options_.headers_use_forward_declarations; } - private: const FileDescriptor* file_; const GenerationOptions& generation_options_; mutable CommonState* common_state_; diff --git a/src/google/protobuf/compiler/objectivec/generator.cc b/src/google/protobuf/compiler/objectivec/generator.cc index 36686a573c..c28a0eecc5 100644 --- a/src/google/protobuf/compiler/objectivec/generator.cc +++ b/src/google/protobuf/compiler/objectivec/generator.cc @@ -73,6 +73,10 @@ bool StringToBool(const std::string& value, bool* result) { return false; } +std::string NumberedObjCMFileName(absl::string_view basename, int number) { + return absl::StrCat(basename, ".out/", number, ".pbobjc.m"); +} + } // namespace bool ObjectiveCGenerator::HasGenerateAll() const { return true; } @@ -251,6 +255,23 @@ bool ObjectiveCGenerator::GenerateAll( options[i].second; return false; } + } else if (options[i].first == "experimental_multi_source_generation") { + // This is an experimental option, and could be removed or change at any + // time; it is not documented in the README.md for that reason. + // + // Enables a mode where each type (message & enum) generates to a unique + // .m file; this is to explore impacts on code size when not + // compiling/linking with `-ObjC` as then only linker visible needs should + // be pulled into the builds. + + if (!StringToBool( + options[i].second, + &generation_options.experimental_multi_source_generation)) { + *error = + "error: Unknown value for experimental_multi_source_generation: " + + options[i].second; + return false; + } } else { *error = "error: Unknown generator option: " + options[i].first; return false; @@ -285,16 +306,14 @@ bool ObjectiveCGenerator::GenerateAll( } FileGenerator::CommonState state; - for (int i = 0; i < files.size(); i++) { - const FileDescriptor* file = files[i]; + for (const auto& file : files) { const FileGenerator file_generator(file, generation_options, state); std::string filepath = FilePath(file); // Generate header. { - std::unique_ptr output( - context->Open(filepath + ".pbobjc.h")); - io::Printer printer(output.get(), '$'); + auto output = absl::WrapUnique(context->Open(filepath + ".pbobjc.h")); + io::Printer printer(output.get()); file_generator.GenerateHeader(&printer); if (printer.failed()) { *error = absl::StrCat("error: internal error generating a header: ", @@ -303,20 +322,63 @@ bool ObjectiveCGenerator::GenerateAll( } } - // Generate m file. - if (!headers_only && !skip_impls.contains(file->name())) { - std::unique_ptr output( - context->Open(filepath + ".pbobjc.m")); - io::Printer printer(output.get(), '$'); - file_generator.GenerateSource(&printer); - if (printer.failed()) { - *error = - absl::StrCat("error: internal error generating an implementation:", - file->name()); - return false; + // Generate m file(s). + if (!headers_only && skip_impls.count(file->name()) == 0) { + if (generation_options.experimental_multi_source_generation) { + int file_number = 0; + + // Generate the Root and FileDescriptor (if needed). + { + std::unique_ptr output( + context->Open(NumberedObjCMFileName(filepath, file_number++))); + io::Printer printer(output.get()); + file_generator.GenerateGlobalSource(&printer); + if (printer.failed()) { + *error = absl::StrCat( + "error: internal error generating an implementation:", + file->name()); + return false; + } + } + + for (int i = 0; i < file_generator.NumEnums(); ++i) { + std::unique_ptr output( + context->Open(NumberedObjCMFileName(filepath, file_number++))); + io::Printer printer(output.get()); + file_generator.GenerateSourceForEnum(i, &printer); + if (printer.failed()) { + *error = absl::StrCat( + "error: internal error generating an enum implementation:", + file->name(), "::", i); + return false; + } + } + + for (int i = 0; i < file_generator.NumMessages(); ++i) { + std::unique_ptr output( + context->Open(NumberedObjCMFileName(filepath, file_number++))); + io::Printer printer(output.get()); + file_generator.GenerateSourceForMessage(i, &printer); + if (printer.failed()) { + *error = absl::StrCat( + "error: internal error generating an message implementation:", + file->name(), "::", i); + return false; + } + } + } else { + auto output = absl::WrapUnique(context->Open(filepath + ".pbobjc.m")); + io::Printer printer(output.get()); + file_generator.GenerateSource(&printer); + if (printer.failed()) { + *error = absl::StrCat( + "error: internal error generating an implementation:", + file->name()); + return false; + } } - } - } + } // if (!headers_only && skip_impls.count(file->name()) == 0) + } // for(file : files) return true; } diff --git a/src/google/protobuf/compiler/objectivec/options.h b/src/google/protobuf/compiler/objectivec/options.h index 388d1cabfe..94ded4ba01 100644 --- a/src/google/protobuf/compiler/objectivec/options.h +++ b/src/google/protobuf/compiler/objectivec/options.h @@ -46,6 +46,7 @@ struct GenerationOptions { // TODO(thomasvl): Eventually flip this default to false for better interop // with Swift if proto usages span modules made from ObjC sources. bool headers_use_forward_declarations = true; + bool experimental_multi_source_generation = false; }; } // namespace objectivec