[ObjC] New experimental option to generate into multiple files.

This generates Messages and Enums into separate files, this is the start of some
exploratory work to see if not using `-ObjC` with the linker can reduce the size
of protos by ensuring there are linker references for all the classes needed.

To take advantage of this, the Application code can't use things like
`NSClassFromString`, as that would not generate any inker reference for usage of
the class.

Again, this is an experimental feature and may change or be removed at any time,
it is not counted as a part of the official options provided by the ObjC
support.

PiperOrigin-RevId: 493595005
pull/11167/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent a9625006c4
commit e173fb6077
  1. 102
      src/google/protobuf/compiler/objectivec/file.cc
  2. 9
      src/google/protobuf/compiler/objectivec/file.h
  3. 98
      src/google/protobuf/compiler/objectivec/generator.cc
  4. 1
      src/google/protobuf/compiler/objectivec/options.h

@ -373,6 +373,92 @@ void FileGenerator::GenerateSource(io::Printer* p) const {
});
}
void FileGenerator::GenerateGlobalSource(io::Printer* p) const {
std::vector<const FileDescriptor*> 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<const FileDescriptor*> extra_files;
for (auto& dep : deps_with_extensions) {
if (!IsDirectDependency(dep, file_)) {
extra_files.push_back(dep);
}
}
absl::btree_set<std::string> fwd_decls;
for (const auto& generator : extension_generators_) {
generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
}
std::vector<std::string> 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<std::string> fwd_decls;
generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
std::vector<std::string> 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<std::string>& 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"

@ -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_;

@ -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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> 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;
}

@ -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

Loading…
Cancel
Save