From 7f993c2b440154150c38345e6e66924c848e4c6e Mon Sep 17 00:00:00 2001 From: Dimitris Koutsogiorgas Date: Fri, 11 Feb 2022 11:05:29 -0800 Subject: [PATCH] Use a objc class prefix mappings path generator option instead. --- objectivec/README.md | 13 ++-- .../objectivec/objectivec_generator.cc | 22 +++---- .../compiler/objectivec/objectivec_helpers.cc | 59 ++++++++++++++++--- .../compiler/objectivec/objectivec_helpers.h | 10 ++-- 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/objectivec/README.md b/objectivec/README.md index b0278f2aee7a..f8024ec8634d 100644 --- a/objectivec/README.md +++ b/objectivec/README.md @@ -133,7 +133,7 @@ This options allow you to provide a custom prefix for all the symbols generated from a proto file (classes (from message), enums, the Root for extension support). -If not set, the generation options `default_objc_class_prefix` and `use_package_as_prefix` +If not set, the generation options `objc_class_prefix_mappings_path` and `use_package_as_prefix` (documented below) control what is used instead. Since Objective C uses a global namespace for all of its classes, there can be collisions. `use_package_as_prefix=yes` should avoid collisions since proto package are used to scope/name things in other @@ -182,11 +182,12 @@ supported keys are: having to add the runtime directory to the header search path since the generate `#import` will be more complete. - * `default_objc_class_prefix`: The default ObjC prefix value to use when - generating sources. The generator will use this if the `objc_class_prefix` - file option is not set. This option can be useful if multiple iOS apps - consume the same proto file but wish to use a different prefix for their - generated sources. + * `objc_class_prefix_mappings_path`: The `value` used for this key + is a path to a file containing a list of prefixes and proto files. + The generator will use this to locate a ObjC class prefix to use when + generating sources unless the `objc_class_prefix` file option is set. + This option can be useful if multiple iOS apps consume a common set of + proto files but wish to use a different prefix for generated sources. * `use_package_as_prefix` and `proto_package_prefix_exceptions_path`: The `value` for `use_package_as_prefix` can be `yes` or `no`, and indicates diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc index b6f53a5a9fab..48956632f70e 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc @@ -190,19 +190,21 @@ bool ObjectiveCGenerator::GenerateAll( // header search path since the generate #import will be more complete. generation_options.runtime_import_prefix = StripSuffixString(options[i].second, "/"); - } else if (options[i].first == "default_objc_class_prefix") { - // The default objc class prefix to use if specified by the command line - // invocation. The file option is always honored first if one is present. - std::string value = options[i].second; - if (value.empty()) { - *error = "error: default_objc_class_prefix cannot be empty."; - return false; - } - SetDefaultObjcClassPrefix(value); + } else if (options[i].first == "objc_class_prefix_mappings_path") { + // Path to use for when loading the objc class prefix mappings to use. + // The `objc_class_prefix` file option is always honored first if one is present. + // + // The format of the file is: + // - An entry is a line of "objc_class_prefix: file.proto, dir/file2.proto". + // - Comments start with "#". + // - A comment can go on a line after a expected package/prefix pair. + // (i.e. - "GPB: descriptor.proto # comment") + // + SetObjCClassPrefixMappingsPath(options[i].second); } else if (options[i].first == "use_package_as_prefix") { // Controls how the symbols should be prefixed to avoid symbols // collisions. The objc_class_prefix file option is always honored first - // followed by the default_objc_class_prefix generator option. This is is just + // followed by the objc_class_prefix_mappings_path generator option. This is is just // what to do if either of these options are not set. The available options are: // "no": Not prefixed (the existing mode). // "yes": Make a prefix out of the proto package. diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc index 7b85c517f2b3..7030ca9deedd 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc @@ -99,8 +99,13 @@ class PrefixModeStorage { public: PrefixModeStorage(); - const std::string default_objc_class_prefix() const { return default_objc_class_prefix_; } - void set_default_objc_class_prefix(const std::string& default_objc_class_prefix) { default_objc_class_prefix_ = default_objc_class_prefix; } + const std::string objc_class_prefix_mappings_path() const { return objc_class_prefix_mappings_path_; } + void set_objc_class_prefix_mappings_path(const std::string& path) { + objc_class_prefix_mappings_path_ = path; + objc_class_prefix_map_.clear(); + } + + std::string objc_class_prefix(const FileDescriptor* file); bool use_package_name() const { return use_package_name_; } void set_use_package_name(bool on_or_off) { use_package_name_ = on_or_off; } @@ -119,7 +124,8 @@ class PrefixModeStorage { private: bool use_package_name_; - std::string default_objc_class_prefix_; + std::map objc_class_prefix_map_; + std::string objc_class_prefix_mappings_path_; std::string exception_path_; std::string forced_prefix_; std::unordered_set exceptions_; @@ -144,6 +150,38 @@ PrefixModeStorage::PrefixModeStorage() { } } +std::string PrefixModeStorage::objc_class_prefix(const FileDescriptor* file) { + if (!file) { + return ""; + } + + if (objc_class_prefix_map_.empty() && !objc_class_prefix_mappings_path_.empty()) { + std::string error_str; + // Re use the same collector as we use for the named framework paths as the file + // is expected to be in the same format. + ImportWriter::ProtoFrameworkCollector collector(&objc_class_prefix_map_); + if (!ParseSimpleFile(objc_class_prefix_mappings_path_, &collector, &error_str)) { + if (error_str.empty()) { + error_str = std::string("protoc:0: warning: Failed to parse") + + std::string(" objc class prefix mappings file: ") + + objc_class_prefix_mappings_path_; + } + std::cerr << error_str << std::endl; + std::cerr.flush(); + objc_class_prefix_map_.clear(); + } + } + + std::map::iterator objc_class_prefix_lookup = + objc_class_prefix_map_.find(file->name()); + + if (objc_class_prefix_lookup != objc_class_prefix_map_.end()) { + return objc_class_prefix_lookup->second; + } + + return ""; +} + bool PrefixModeStorage::is_package_exempted(const std::string& package) { if (exceptions_.empty() && !exception_path_.empty()) { std::string error_str; @@ -173,8 +211,12 @@ PrefixModeStorage g_prefix_mode; } // namespace -void SetDefaultObjcClassPrefix(const std::string& default_objc_class_prefix) { - g_prefix_mode.set_default_objc_class_prefix(default_objc_class_prefix); +std::string GetObjCClassPrefixMappingsPath() { + return g_prefix_mode.objc_class_prefix_mappings_path(); +} + +void SetObjCClassPrefixMappingsPath(const std::string& file_path) { + g_prefix_mode.set_objc_class_prefix_mappings_path(file_path); } bool UseProtoPackageAsDefaultPrefix() { @@ -539,9 +581,10 @@ std::string FileClassPrefix(const FileDescriptor* file) { return file->options().objc_class_prefix(); } - // If a default prefix is passed through objc_opt then accept it. - if (!g_prefix_mode.default_objc_class_prefix().empty()) { - return g_prefix_mode.default_objc_class_prefix(); + // If package prefix is specified in an objc class prefix mapping file then use that. + std::string objc_class_prefix = g_prefix_mode.objc_class_prefix(file); + if (!objc_class_prefix.empty()) { + return objc_class_prefix; } // If package prefix isn't enabled, done. diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h index 418ffaba5e18..caf8d7664deb 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h @@ -47,9 +47,10 @@ namespace protobuf { namespace compiler { namespace objectivec { -// Set the default objc class prefix that should be used. This method is used only -// when default_objc_class_prefix is passed through objc_opt. -void PROTOC_EXPORT SetDefaultObjcClassPrefix(const std::string& objc_class_prefix); +// Get/Set the path to a file to load for objc class prefix lookups. +std::string PROTOC_EXPORT GetObjCClassPrefixMappingsPath(); +void PROTOC_EXPORT SetObjCClassPrefixMappingsPath( + const std::string& file_path); // Get/Set if the proto package should be used to make the default prefix for // symbols. This will then impact most of the type naming apis below. It is done // as a global to not break any other generator reusing the methods since they @@ -71,7 +72,7 @@ struct Options { std::string generate_for_named_framework; std::string named_framework_to_proto_path_mappings_path; std::string runtime_import_prefix; - std::string default_objc_class_prefix; + std::string objc_class_prefix_mappings_path; bool prefixes_must_be_registered; bool require_prefixes; }; @@ -315,7 +316,6 @@ class PROTOC_EXPORT ImportWriter { const std::string& runtime_import_prefix, bool default_cpp_symbol = false); - private: class ProtoFrameworkCollector : public LineConsumer { public: ProtoFrameworkCollector(std::map* inout_proto_file_to_framework_name)