Add `prefix_to_proto_package_mappings_path` ObjC option.

pull/9537/head
Dimitris Koutsogiorgas 3 years ago committed by Thomas Van Lenten
parent d0c06bcd93
commit a112c4ab96
  1. 30
      objectivec/README.md
  2. 15
      src/google/protobuf/compiler/objectivec/objectivec_generator.cc
  3. 84
      src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
  4. 4
      src/google/protobuf/compiler/objectivec/objectivec_helpers.h

@ -133,12 +133,12 @@ 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 from a proto file (classes (from message), enums, the Root for extension
support). support).
If not set, the generation option `use_package_as_prefix` (documented below) If not set, the generation options `prefix_to_proto_package_mappings_path` and
controls what is used instead. Since Objective C uses a global namespace for all `use_package_as_prefix` (documented below) controls what is used instead. Since
of its classes, there can be collisions. `use_package_as_prefix=yes` should Objective C uses a global namespace for all of its classes, there can be collisions.
avoid collisions since proto package are used to scope/name things in other `use_package_as_prefix=yes` should avoid collisions since proto package are used to
languages, but this option can be used to get shorter names instead. Convention scope/name things in other languages, but this option can be used to get shorter
is to base the explicit prefix on the proto package. names instead. Convention is to base the explicit prefix on the proto package.
Objective C Generator `protoc` Options Objective C Generator `protoc` Options
-------------------------------------- --------------------------------------
@ -182,6 +182,24 @@ supported keys are:
having to add the runtime directory to the header search path since the having to add the runtime directory to the header search path since the
generate `#import` will be more complete. generate `#import` will be more complete.
* `prefix_to_proto_package_mappings_path`: The `value` used for
this key is a path to a file containing a list of prefixes and proto packages.
The generator will use this to locate which ObjC class prefix to use when
generating sources _unless_ the `objc_class_prefix` file option is set.
This option can be useful if multiple apps consume a common set of
proto files but wish to use a different prefix for the generated sources
between them. This option takes precedent over the `use_package_as_prefix`
option.
The format of the file is:
* An entry is a line of "package=prefix".
* Comments start with `#`.
* A comment can go on a line after a expected package/prefix pair.
(i.e. - "package=prefix # comment")
* For files that do NOT have a proto package (not recommended), an
entry can be made as "no_package:PATH=prefix", where PATH is the
path for the .proto file.
* `use_package_as_prefix` and `proto_package_prefix_exceptions_path`: The * `use_package_as_prefix` and `proto_package_prefix_exceptions_path`: The
`value` for `use_package_as_prefix` can be `yes` or `no`, and indicates `value` for `use_package_as_prefix` can be `yes` or `no`, and indicates
if a prefix should be derived from the proto package for all the symbols if a prefix should be derived from the proto package for all the symbols

@ -190,6 +190,21 @@ bool ObjectiveCGenerator::GenerateAll(
// header search path since the generate #import will be more complete. // header search path since the generate #import will be more complete.
generation_options.runtime_import_prefix = generation_options.runtime_import_prefix =
StripSuffixString(options[i].second, "/"); StripSuffixString(options[i].second, "/");
} else if (options[i].first == "prefix_to_proto_package_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.
// This option also has precedent over the use_package_as_prefix option.
//
// The format of the file is:
// - An entry is a line of "package=prefix".
// - Comments start with "#".
// - A comment can go on a line after a expected package/prefix pair.
// (i.e. - "package=prefix # comment")
// - For files that do NOT have a proto package (not recommended), an
// entry can be made as "no_package:PATH=prefix", where PATH is the
// path for the .proto file.
//
SetPrefixToProtoPackageMappingsPath(options[i].second);
} else if (options[i].first == "use_package_as_prefix") { } else if (options[i].first == "use_package_as_prefix") {
// Controls how the symbols should be prefixed to avoid symbols // Controls how the symbols should be prefixed to avoid symbols
// collisions. The objc_class_prefix file option is always honored, this // collisions. The objc_class_prefix file option is always honored, this

@ -95,10 +95,29 @@ class SimpleLineCollector : public LineConsumer {
std::unordered_set<std::string>* set_; std::unordered_set<std::string>* set_;
}; };
class ExpectedPrefixesCollector : public LineConsumer {
public:
ExpectedPrefixesCollector(std::map<std::string, std::string>* inout_package_to_prefix_map)
: prefix_map_(inout_package_to_prefix_map) {}
virtual bool ConsumeLine(const StringPiece& line, std::string* out_error) override;
private:
std::map<std::string, std::string>* prefix_map_;
};
class PrefixModeStorage { class PrefixModeStorage {
public: public:
PrefixModeStorage(); PrefixModeStorage();
const std::string prefix_to_proto_package_mappings_path() const { return prefix_to_proto_package_mappings_path_; }
void set_prefix_to_proto_package_mappings_path(const std::string& path) {
prefix_to_proto_package_mappings_path_ = path;
prefix_to_proto_package_map_.clear();
}
std::string prefix_from_proto_package_mappings(const FileDescriptor* file);
bool use_package_name() const { return use_package_name_; } bool use_package_name() const { return use_package_name_; }
void set_use_package_name(bool on_or_off) { use_package_name_ = on_or_off; } void set_use_package_name(bool on_or_off) { use_package_name_ = on_or_off; }
@ -116,6 +135,8 @@ class PrefixModeStorage {
private: private:
bool use_package_name_; bool use_package_name_;
std::map<std::string, std::string> prefix_to_proto_package_map_;
std::string prefix_to_proto_package_mappings_path_;
std::string exception_path_; std::string exception_path_;
std::string forced_prefix_; std::string forced_prefix_;
std::unordered_set<std::string> exceptions_; std::unordered_set<std::string> exceptions_;
@ -140,6 +161,44 @@ PrefixModeStorage::PrefixModeStorage() {
} }
} }
std::string PrefixModeStorage::prefix_from_proto_package_mappings(const FileDescriptor* file) {
if (!file) {
return "";
}
if (prefix_to_proto_package_map_.empty() && !prefix_to_proto_package_mappings_path_.empty()) {
std::string error_str;
// Re use the same collector as we use for expected_prefixes_path since the file
// format is the same.
ExpectedPrefixesCollector collector(&prefix_to_proto_package_map_);
if (!ParseSimpleFile(prefix_to_proto_package_mappings_path_, &collector, &error_str)) {
if (error_str.empty()) {
error_str = std::string("protoc:0: warning: Failed to parse")
+ std::string(" prefix to proto package mappings file: ")
+ prefix_to_proto_package_mappings_path_;
}
std::cerr << error_str << std::endl;
std::cerr.flush();
prefix_to_proto_package_map_.clear();
}
}
const std::string package = file->package();
// For files without packages, the can be registered as "no_package:PATH",
// allowing the expected prefixes file.
static const std::string no_package_prefix("no_package:");
const std::string lookup_key = package.empty() ? no_package_prefix + file->name() : package;
std::map<std::string, std::string>::const_iterator prefix_lookup =
prefix_to_proto_package_map_.find(lookup_key);
if (prefix_lookup != prefix_to_proto_package_map_.end()) {
return prefix_lookup->second;
}
return "";
}
bool PrefixModeStorage::is_package_exempted(const std::string& package) { bool PrefixModeStorage::is_package_exempted(const std::string& package) {
if (exceptions_.empty() && !exception_path_.empty()) { if (exceptions_.empty() && !exception_path_.empty()) {
std::string error_str; std::string error_str;
@ -169,6 +228,14 @@ PrefixModeStorage g_prefix_mode;
} // namespace } // namespace
std::string GetPrefixToProtoPackageMappingsPath() {
return g_prefix_mode.prefix_to_proto_package_mappings_path();
}
void SetPrefixToProtoPackageMappingsPath(const std::string& file_path) {
g_prefix_mode.set_prefix_to_proto_package_mappings_path(file_path);
}
bool UseProtoPackageAsDefaultPrefix() { bool UseProtoPackageAsDefaultPrefix() {
return g_prefix_mode.use_package_name(); return g_prefix_mode.use_package_name();
} }
@ -531,6 +598,12 @@ std::string FileClassPrefix(const FileDescriptor* file) {
return file->options().objc_class_prefix(); return file->options().objc_class_prefix();
} }
// If package prefix is specified in an prefix to proto mappings file then use that.
std::string objc_class_prefix = g_prefix_mode.prefix_from_proto_package_mappings(file);
if (!objc_class_prefix.empty()) {
return objc_class_prefix;
}
// If package prefix isn't enabled, done. // If package prefix isn't enabled, done.
if (!g_prefix_mode.use_package_name()) { if (!g_prefix_mode.use_package_name()) {
return ""; return "";
@ -1205,17 +1278,6 @@ void RemoveComment(StringPiece* input) {
namespace { namespace {
class ExpectedPrefixesCollector : public LineConsumer {
public:
ExpectedPrefixesCollector(std::map<std::string, std::string>* inout_package_to_prefix_map)
: prefix_map_(inout_package_to_prefix_map) {}
virtual bool ConsumeLine(const StringPiece& line, std::string* out_error) override;
private:
std::map<std::string, std::string>* prefix_map_;
};
bool ExpectedPrefixesCollector::ConsumeLine( bool ExpectedPrefixesCollector::ConsumeLine(
const StringPiece& line, std::string* out_error) { const StringPiece& line, std::string* out_error) {
int offset = line.find('='); int offset = line.find('=');

@ -47,6 +47,10 @@ namespace protobuf {
namespace compiler { namespace compiler {
namespace objectivec { namespace objectivec {
// Get/Set the path to a file to load for objc class prefix lookups.
std::string PROTOC_EXPORT GetPrefixToProtoPackageMappingsPath();
void PROTOC_EXPORT SetPrefixToProtoPackageMappingsPath(
const std::string& file_path);
// Get/Set if the proto package should be used to make the default prefix for // 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 // 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 // as a global to not break any other generator reusing the methods since they

Loading…
Cancel
Save