blob: ca0718bed6e2dd358a8e6d5343de6939d66cd1f2 [file] [log] [blame]
// 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/import_writer.h"
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/log/absl_check.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "google/protobuf/compiler/objectivec/line_consumer.h"
#include "google/protobuf/compiler/objectivec/names.h"
#include "google/protobuf/io/printer.h"
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
// error cases, so it seems to be ok to use as a back door for errors.
namespace google {
namespace protobuf {
namespace compiler {
namespace objectivec {
namespace {
class ProtoFrameworkCollector : public LineConsumer {
public:
explicit ProtoFrameworkCollector(
absl::flat_hash_map<std::string, std::string>*
inout_proto_file_to_framework_name)
: map_(inout_proto_file_to_framework_name) {}
bool ConsumeLine(absl::string_view line, std::string* out_error) override;
private:
absl::flat_hash_map<std::string, std::string>* map_;
};
bool ProtoFrameworkCollector::ConsumeLine(absl::string_view line,
std::string* out_error) {
int offset = line.find(':');
if (offset == absl::string_view::npos) {
*out_error = absl::StrCat(
"Framework/proto file mapping line without colon sign: '", line, "'.");
return false;
}
absl::string_view framework_name =
absl::StripAsciiWhitespace(line.substr(0, offset));
absl::string_view proto_file_list =
absl::StripAsciiWhitespace(line.substr(offset + 1));
int start = 0;
while (start < proto_file_list.length()) {
offset = proto_file_list.find(',', start);
if (offset == absl::string_view::npos) {
offset = proto_file_list.length();
}
absl::string_view proto_file = absl::StripAsciiWhitespace(
proto_file_list.substr(start, offset - start));
if (!proto_file.empty()) {
auto existing_entry = map_->find(proto_file);
if (existing_entry != map_->end()) {
std::cerr << "warning: duplicate proto file reference, replacing "
"framework entry for '"
<< proto_file << "' with '" << framework_name << "' (was '"
<< existing_entry->second << "')." << std::endl;
std::cerr.flush();
}
if (absl::StrContains(proto_file, ' ')) {
std::cerr << "note: framework mapping file had a proto file with a "
"space in, hopefully that isn't a missing comma: '"
<< proto_file << "'" << std::endl;
std::cerr.flush();
}
(*map_)[proto_file] = std::string(framework_name);
}
start = offset + 1;
}
return true;
}
} // namespace
ImportWriter::ImportWriter(
const std::string& generate_for_named_framework,
const std::string& named_framework_to_proto_path_mappings_path,
const std::string& runtime_import_prefix, bool for_bundled_proto)
: generate_for_named_framework_(generate_for_named_framework),
named_framework_to_proto_path_mappings_path_(
named_framework_to_proto_path_mappings_path),
runtime_import_prefix_(runtime_import_prefix),
for_bundled_proto_(for_bundled_proto),
need_to_parse_mapping_file_(true) {}
void ImportWriter::AddFile(const FileDescriptor* file,
const std::string& header_extension) {
if (IsProtobufLibraryBundledProtoFile(file)) {
// The imports of the WKTs are only needed within the library itself,
// in other cases, they get skipped because the generated code already
// import GPBProtocolBuffers.h and hence proves them.
if (for_bundled_proto_) {
protobuf_imports_.emplace_back(
absl::StrCat("GPB", FilePathBasename(file), header_extension));
}
return;
}
auto module_name = ModuleForFile(file);
if (!module_name.empty()) {
other_framework_imports_.emplace_back(absl::StrCat(
module_name, "/", FilePathBasename(file), header_extension));
return;
}
if (!generate_for_named_framework_.empty()) {
other_framework_imports_.push_back(
absl::StrCat(generate_for_named_framework_, "/", FilePathBasename(file),
header_extension));
return;
}
other_imports_.push_back(FilePath(file) + header_extension);
}
void ImportWriter::AddRuntimeImport(const std::string& header_name) {
protobuf_imports_.push_back(header_name);
}
std::string ImportWriter::ModuleForFile(const FileDescriptor* file) {
ABSL_DCHECK(!IsProtobufLibraryBundledProtoFile(file));
// Lazy parse any mappings.
if (need_to_parse_mapping_file_) {
ParseFrameworkMappings();
}
auto proto_lookup = proto_file_to_framework_name_.find(file->name());
if (proto_lookup != proto_file_to_framework_name_.end()) {
return proto_lookup->second;
}
return "";
}
void ImportWriter::PrintFileImports(io::Printer* p) const {
if (!other_framework_imports_.empty()) {
for (const auto& header : other_framework_imports_) {
p->Print("#import <$header$>\n", "header", header);
}
}
if (!other_imports_.empty()) {
if (!other_framework_imports_.empty()) {
p->Print("\n");
}
for (const auto& header : other_imports_) {
p->Print("#import \"$header$\"\n", "header", header);
}
}
}
void ImportWriter::PrintRuntimeImports(io::Printer* p,
bool default_cpp_symbol) const {
// Given an override, use that.
if (!runtime_import_prefix_.empty()) {
for (const auto& header : protobuf_imports_) {
p->Print("#import \"$import_prefix$/$header$\"\n", "header", header,
"import_prefix", runtime_import_prefix_);
}
return;
}
// If bundled, no need to do the framework support below.
if (for_bundled_proto_) {
ABSL_DCHECK(!default_cpp_symbol);
for (const auto& header : protobuf_imports_) {
p->Print("#import \"$header$\"\n", "header", header);
}
return;
}
const std::string cpp_symbol(
ProtobufFrameworkImportSymbol(ProtobufLibraryFrameworkName));
if (default_cpp_symbol) {
p->Print(
// clang-format off
"// This CPP symbol can be defined to use imports that match up to the framework\n"
"// imports needed when using CocoaPods.\n"
"#if !defined($cpp_symbol$)\n"
" #define $cpp_symbol$ 0\n"
"#endif\n"
"\n",
// clang-format on
"cpp_symbol", cpp_symbol);
}
p->Print("#if $cpp_symbol$\n", "cpp_symbol", cpp_symbol);
for (const auto& header : protobuf_imports_) {
p->Print(" #import <$framework_name$/$header$>\n", "framework_name",
ProtobufLibraryFrameworkName, "header", header);
}
p->Print("#else\n");
for (const auto& header : protobuf_imports_) {
p->Print(" #import \"$header$\"\n", "header", header);
}
p->Print("#endif\n");
}
void ImportWriter::ParseFrameworkMappings() {
need_to_parse_mapping_file_ = false;
if (named_framework_to_proto_path_mappings_path_.empty()) {
return; // Nothing to do.
}
ProtoFrameworkCollector collector(&proto_file_to_framework_name_);
std::string parse_error;
if (!ParseSimpleFile(named_framework_to_proto_path_mappings_path_, &collector,
&parse_error)) {
std::cerr << "error parsing "
<< named_framework_to_proto_path_mappings_path_ << " : "
<< parse_error << std::endl;
std::cerr.flush();
}
}
} // namespace objectivec
} // namespace compiler
} // namespace protobuf
} // namespace google