diff options
Diffstat (limited to 'contrib/libs/protoc/src/google/protobuf/compiler/objectivec/generator.cc')
-rw-r--r-- | contrib/libs/protoc/src/google/protobuf/compiler/objectivec/generator.cc | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/contrib/libs/protoc/src/google/protobuf/compiler/objectivec/generator.cc b/contrib/libs/protoc/src/google/protobuf/compiler/objectivec/generator.cc new file mode 100644 index 0000000000..31c99dba67 --- /dev/null +++ b/contrib/libs/protoc/src/google/protobuf/compiler/objectivec/generator.cc @@ -0,0 +1,402 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "google/protobuf/compiler/objectivec/generator.h" + +#include <fstream> +#include <iostream> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "y_absl/container/flat_hash_set.h" +#include "y_absl/strings/ascii.h" +#include "y_absl/strings/str_cat.h" +#include "y_absl/strings/str_split.h" +#include "y_absl/strings/strip.h" +#include "google/protobuf/compiler/objectivec/file.h" +#include "google/protobuf/compiler/objectivec/names.h" +#include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { + +// Convert a string with "yes"/"no" (case insensitive) to a boolean, returning +// true/false for if the input string was a valid value. The empty string is +// also treated as a true value. If the input string is invalid, `result` is +// unchanged. +bool StringToBool(const TProtoStringType& value, bool* result) { + TProtoStringType upper_value(value); + y_absl::AsciiStrToUpper(&upper_value); + if (upper_value == "NO") { + *result = false; + return true; + } + if (upper_value == "YES" || upper_value.empty()) { + *result = true; + return true; + } + + return false; +} + +TProtoStringType NumberedObjCMFileName(y_absl::string_view basename, int number) { + return y_absl::StrCat(basename, ".out/", number, ".pbobjc.m"); +} + +} // namespace + +bool ObjectiveCGenerator::HasGenerateAll() const { return true; } + +bool ObjectiveCGenerator::Generate(const FileDescriptor* file, + const TProtoStringType& parameter, + GeneratorContext* context, + TProtoStringType* error) const { + *error = "Unimplemented Generate() method. Call GenerateAll() instead."; + return false; +} + +bool ObjectiveCGenerator::GenerateAll( + const std::vector<const FileDescriptor*>& files, + const TProtoStringType& parameter, GeneratorContext* context, + TProtoStringType* error) const { + // ----------------------------------------------------------------- + // Parse generator options. These options are passed to the compiler using the + // --objc_opt flag. The options are passed as a comma separated list of + // options along with their values. If the option appears multiple times, only + // the last value will be considered. + // + // e.g. protoc ... + // --objc_opt=expected_prefixes=file.txt,generate_for_named_framework=MyFramework + + Options validation_options; + GenerationOptions generation_options; + + std::vector<std::pair<TProtoStringType, TProtoStringType> > options; + ParseGeneratorParameter(parameter, &options); + for (size_t i = 0; i < options.size(); i++) { + if (options[i].first == "expected_prefixes_path") { + // Path to find a file containing the expected prefixes + // (objc_class_prefix "PREFIX") for proto packages (package NAME). The + // generator will then issue warnings/errors if in the proto files being + // generated the option is not listed/wrong/etc in the file. + // + // 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. + // + // There is no validation that the prefixes are good prefixes, it is + // assumed that they are when you create the file. + validation_options.expected_prefixes_path = options[i].second; + } else if (options[i].first == "expected_prefixes_suppressions") { + // A semicolon delimited string that lists the paths of .proto files to + // exclude from the package prefix validations (expected_prefixes_path). + // This is provided as an "out", to skip some files being checked. + for (y_absl::string_view split_piece : + y_absl::StrSplit(options[i].second, ';', y_absl::SkipEmpty())) { + validation_options.expected_prefixes_suppressions.push_back( + TProtoStringType(split_piece)); + } + } else if (options[i].first == "prefixes_must_be_registered") { + // If objc prefix file option value must be registered to be used. This + // option has no meaning if an "expected_prefixes_path" isn't set. The + // available options are: + // "no": They don't have to be registered. + // "yes": They must be registered and an error will be raised if a files + // tried to use a prefix that isn't registered. + // Default is "no". + if (!StringToBool(options[i].second, + &validation_options.prefixes_must_be_registered)) { + *error = y_absl::StrCat( + "error: Unknown value for prefixes_must_be_registered: ", + options[i].second); + return false; + } + } else if (options[i].first == "require_prefixes") { + // If every file must have an objc prefix file option to be used. The + // available options are: + // "no": Files can be generated without the prefix option. + // "yes": Files must have the objc prefix option, and an error will be + // raised if a files doesn't have one. + // Default is "no". + if (!StringToBool(options[i].second, + &validation_options.require_prefixes)) { + *error = y_absl::StrCat("error: Unknown value for require_prefixes: ", + options[i].second); + return false; + } + } else if (options[i].first == "generate_for_named_framework") { + // The name of the framework that protos are being generated for. This + // will cause the #import statements to be framework based using this + // name (i.e. - "#import <NAME/proto.pbobjc.h>). + // + // NOTE: If this option is used with + // named_framework_to_proto_path_mappings_path, then this is effectively + // the "default" framework name used for everything that wasn't mapped by + // the mapping file. + generation_options.generate_for_named_framework = options[i].second; + } else if (options[i].first == + "named_framework_to_proto_path_mappings_path") { + // Path to find a file containing the list of framework names and proto + // files. The generator uses this to decide if a proto file + // referenced should use a framework style import vs. a user level import + // (#import <FRAMEWORK/file.pbobjc.h> vs #import "dir/file.pbobjc.h"). + // + // The format of the file is: + // - An entry is a line of "frameworkName: file.proto, dir/file2.proto". + // - Comments start with "#". + // - A comment can go on a line after a expected package/prefix pair. + // (i.e. - "frameworkName: file.proto # comment") + // + // Any number of files can be listed for a framework, just separate them + // with commas. + // + // There can be multiple lines listing the same frameworkName in case it + // has a lot of proto files included in it; having multiple lines makes + // things easier to read. If a proto file is not configured in the + // mappings file, it will use the default framework name if one was passed + // with generate_for_named_framework, or the relative path to it's include + // path otherwise. + generation_options.named_framework_to_proto_path_mappings_path = + options[i].second; + } else if (options[i].first == "runtime_import_prefix") { + // Path to use as a prefix on #imports of runtime provided headers in the + // generated files. When integrating ObjC protos into a build system, + // this can be used to avoid having to add the runtime directory to the + // header search path since the generate #import will be more complete. + generation_options.runtime_import_prefix = + TProtoStringType(y_absl::StripSuffix(options[i].second, "/")); + } else if (options[i].first == "package_to_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. 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. + // + SetPackageToPrefixMappingsPath(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, this + // is just what to do if that isn't set. The available options are: + // "no": Not prefixed (the existing mode). + // "yes": Make a prefix out of the proto package. + bool value = false; + if (StringToBool(options[i].second, &value)) { + SetUseProtoPackageAsDefaultPrefix(value); + } else { + *error = y_absl::StrCat("error: Unknown use_package_as_prefix: ", + options[i].second); + return false; + } + } else if (options[i].first == "proto_package_prefix_exceptions_path") { + // Path to find a file containing the list of proto package names that are + // exceptions when use_package_as_prefix is enabled. This can be used to + // migrate packages one at a time to use_package_as_prefix since there + // are likely code updates needed with each one. + // + // The format of the file is: + // - An entry is a line of "proto.package.name". + // - Comments start with "#". + // - A comment can go on a line after a expected package/prefix pair. + // (i.e. - "some.proto.package # comment") + SetProtoPackagePrefixExceptionList(options[i].second); + } else if (options[i].first == "package_as_prefix_forced_prefix") { + // String to use as the prefix when deriving a prefix from the package + // name. So this only applies when use_package_as_prefix is also used. + SetForcedPackagePrefix(options[i].second); + } else if (options[i].first == "headers_use_forward_declarations") { + if (!StringToBool(options[i].second, + &generation_options.headers_use_forward_declarations)) { + *error = y_absl::StrCat( + "error: Unknown value for headers_use_forward_declarations: ", + 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 ObjC class (messages and roots) 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 = y_absl::StrCat( + "error: Unknown value for experimental_multi_source_generation: ", + options[i].second); + return false; + } + } else { + *error = + y_absl::StrCat("error: Unknown generator option: ", options[i].first); + return false; + } + } + + // Multi source generation forces off the use of fwd decls in favor of + // imports. + if (generation_options.experimental_multi_source_generation) { + generation_options.headers_use_forward_declarations = false; + } + + // ----------------------------------------------------------------- + + // These are not official generation options and could be removed/changed in + // the future and doing that won't count as a breaking change. + bool headers_only = getenv("GPB_OBJC_HEADERS_ONLY") != nullptr; + y_absl::flat_hash_set<TProtoStringType> skip_impls; + if (getenv("GPB_OBJC_SKIP_IMPLS_FILE") != nullptr) { + std::ifstream skip_file(getenv("GPB_OBJC_SKIP_IMPLS_FILE")); + if (skip_file.is_open()) { + std::string line; + while (std::getline(skip_file, line)) { + skip_impls.insert(line.c_str()); + } + } else { + *error = "error: Failed to open GPB_OBJC_SKIP_IMPLS_FILE file"; + return false; + } + } + + // ----------------------------------------------------------------- + + // Validate the objc prefix/package pairings. + if (!ValidateObjCClassPrefixes(files, validation_options, error)) { + // *error will have been filled in. + return false; + } + + FileGenerator::CommonState state; + for (const auto& file : files) { + const FileGenerator file_generator(file, generation_options, state); + TProtoStringType filepath = FilePath(file); + + // Generate header. + { + auto output = + y_absl::WrapUnique(context->Open(y_absl::StrCat(filepath, ".pbobjc.h"))); + io::Printer printer(output.get()); + file_generator.GenerateHeader(&printer); + if (printer.failed()) { + *error = y_absl::StrCat("error: internal error generating a header: ", + 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 = y_absl::StrCat( + "error: internal error generating an implementation:", + file->name()); + return false; + } + } + + // Enums only generate C functions, so they can all go in one file as + // dead stripping anything not used. + if (file_generator.NumEnums() > 0) { + std::unique_ptr<io::ZeroCopyOutputStream> output( + context->Open(NumberedObjCMFileName(filepath, file_number++))); + io::Printer printer(output.get()); + file_generator.GenerateSourceForEnums(&printer); + if (printer.failed()) { + *error = y_absl::StrCat( + "error: internal error generating an enum implementation(s):", + file->name()); + 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 = y_absl::StrCat( + "error: internal error generating an message implementation:", + file->name(), "::", i); + return false; + } + } + } else { + auto output = y_absl::WrapUnique( + context->Open(y_absl::StrCat(filepath, ".pbobjc.m"))); + io::Printer printer(output.get()); + file_generator.GenerateSource(&printer); + if (printer.failed()) { + *error = y_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; +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google |