Files
flatbuffers/src/idl_gen_grpc.cpp
Aman Priyadarshi 468c00a3fe Rebased: grpc/compiler: Respect filename suffix and extension during code generation (#7414)
* grpc/compiler: Respect filename suffix and extension during code generation

grpc compiler is not respecting filename suffix and extension passed to
flatc CLI. This causes compiler to spit out incorrect code, which then
cannot be compiled without modification.

Following patch fixes the problem.

Note, I ended up removing some code introduced #6954 ("Have grpc include
file with correct filename-suffix given to flatc") in favour of keeping
sanity of the generator code.

Signed-off-by: Aman Priyadarshi <aman.eureka@gmail.com>

* tests: Add filename-suffix and filename-ext test files

* Test 1: Filename extension changed to "hpp".
* Test 2: Filename suffix changed to "_suffix".
* Test 3: Filename extension changed to "hpp" and suffix changed to "_suffix"

Signed-off-by: Aman Priyadarshi <aman.eureka@gmail.com>
2022-08-07 11:32:53 -07:00

561 lines
18 KiB
C++

/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// independent from idl_parser, since this code is not needed for most clients
#include "flatbuffers/code_generators.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#include "src/compiler/cpp_generator.h"
#include "src/compiler/go_generator.h"
#include "src/compiler/java_generator.h"
#include "src/compiler/python_generator.h"
#include "src/compiler/swift_generator.h"
#include "src/compiler/ts_generator.h"
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4512) // C4512: 'class' : assignment operator could
// not be generated
#endif
namespace flatbuffers {
class FlatBufMethod : public grpc_generator::Method {
public:
enum Streaming { kNone, kClient, kServer, kBiDi };
FlatBufMethod(const RPCCall *method) : method_(method) {
streaming_ = kNone;
auto val = method_->attributes.Lookup("streaming");
if (val) {
if (val->constant == "client") streaming_ = kClient;
if (val->constant == "server") streaming_ = kServer;
if (val->constant == "bidi") streaming_ = kBiDi;
}
}
grpc::string GetLeadingComments(const grpc::string) const { return ""; }
grpc::string GetTrailingComments(const grpc::string) const { return ""; }
std::vector<grpc::string> GetAllComments() const {
return method_->doc_comment;
}
std::string name() const { return method_->name; }
// TODO: This method need to incorporate namespace for C++ side. Other
// language bindings simply don't use this method.
std::string GRPCType(const StructDef &sd) const {
return "flatbuffers::grpc::Message<" + sd.name + ">";
}
std::vector<std::string> get_input_namespace_parts() const {
return (*method_->request).defined_namespace->components;
}
std::string get_input_type_name() const { return (*method_->request).name; }
std::vector<std::string> get_output_namespace_parts() const {
return (*method_->response).defined_namespace->components;
}
std::string get_output_type_name() const { return (*method_->response).name; }
bool get_module_and_message_path_input(grpc::string * /*str*/,
grpc::string /*generator_file_name*/,
bool /*generate_in_pb2_grpc*/,
grpc::string /*import_prefix*/) const {
return true;
}
bool get_module_and_message_path_output(
grpc::string * /*str*/, grpc::string /*generator_file_name*/,
bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
return true;
}
std::string get_fb_builder() const { return "builder"; }
std::string input_type_name() const { return GRPCType(*method_->request); }
std::string output_type_name() const { return GRPCType(*method_->response); }
bool NoStreaming() const { return streaming_ == kNone; }
bool ClientStreaming() const { return streaming_ == kClient; }
bool ServerStreaming() const { return streaming_ == kServer; }
bool BidiStreaming() const { return streaming_ == kBiDi; }
private:
const RPCCall *method_;
Streaming streaming_;
};
class FlatBufService : public grpc_generator::Service {
public:
FlatBufService(const ServiceDef *service) : service_(service) {}
grpc::string GetLeadingComments(const grpc::string) const { return ""; }
grpc::string GetTrailingComments(const grpc::string) const { return ""; }
std::vector<grpc::string> GetAllComments() const {
return service_->doc_comment;
}
std::vector<grpc::string> namespace_parts() const {
return service_->defined_namespace->components;
}
std::string name() const { return service_->name; }
bool is_internal() const {
return service_->Definition::attributes.Lookup("private") ? true : false;
}
int method_count() const {
return static_cast<int>(service_->calls.vec.size());
}
std::unique_ptr<const grpc_generator::Method> method(int i) const {
return std::unique_ptr<const grpc_generator::Method>(
new FlatBufMethod(service_->calls.vec[i]));
}
private:
const ServiceDef *service_;
};
class FlatBufPrinter : public grpc_generator::Printer {
public:
FlatBufPrinter(std::string *str, const char indentation_type)
: str_(str),
escape_char_('$'),
indent_(0),
indentation_size_(2),
indentation_type_(indentation_type) {}
void Print(const std::map<std::string, std::string> &vars,
const char *string_template) {
std::string s = string_template;
// Replace any occurrences of strings in "vars" that are surrounded
// by the escape character by what they're mapped to.
size_t pos;
while ((pos = s.find(escape_char_)) != std::string::npos) {
// Found an escape char, must also find the closing one.
size_t pos2 = s.find(escape_char_, pos + 1);
// If placeholder not closed, ignore.
if (pos2 == std::string::npos) break;
auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
// If unknown placeholder, ignore.
if (it == vars.end()) break;
// Subtitute placeholder.
s.replace(pos, pos2 - pos + 1, it->second);
}
Print(s.c_str());
}
void Print(const char *s) {
if (s == nullptr || *s == '\0') { return; }
// Add this string, but for each part separated by \n, add indentation.
for (;;) {
// Current indentation.
str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_);
// See if this contains more than one line.
const char *lf = strchr(s, '\n');
if (lf) {
(*str_) += std::string(s, lf + 1);
s = lf + 1;
if (!*s) break; // Only continue if there's more lines.
} else {
(*str_) += s;
break;
}
}
}
void SetIndentationSize(const size_t size) {
FLATBUFFERS_ASSERT(str_->empty());
indentation_size_ = size;
}
void Indent() { indent_++; }
void Outdent() {
FLATBUFFERS_ASSERT(indent_ > 0);
indent_--;
}
private:
std::string *str_;
char escape_char_;
size_t indent_;
size_t indentation_size_;
char indentation_type_;
};
class FlatBufFile : public grpc_generator::File {
public:
enum Language {
kLanguageGo,
kLanguageCpp,
kLanguageJava,
kLanguagePython,
kLanguageSwift,
kLanguageTS
};
FlatBufFile(const Parser &parser, const std::string &file_name,
Language language)
: parser_(parser), file_name_(file_name), language_(language) {}
FlatBufFile &operator=(const FlatBufFile &);
grpc::string GetLeadingComments(const grpc::string) const { return ""; }
grpc::string GetTrailingComments(const grpc::string) const { return ""; }
std::vector<grpc::string> GetAllComments() const {
return std::vector<grpc::string>();
}
std::string filename() const { return file_name_; }
std::string filename_without_ext() const {
return StripExtension(file_name_);
}
std::string package() const {
return parser_.current_namespace_->GetFullyQualifiedName("");
}
std::vector<std::string> package_parts() const {
return parser_.current_namespace_->components;
}
std::string additional_headers() const {
switch (language_) {
case kLanguageCpp: {
return "#include \"flatbuffers/grpc.h\"\n";
}
case kLanguageGo: {
return "import \"github.com/google/flatbuffers/go\"";
}
case kLanguageJava: {
return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
}
case kLanguagePython: {
return "";
}
case kLanguageSwift: {
return "";
}
case kLanguageTS: {
return "";
}
}
return "";
}
int service_count() const {
return static_cast<int>(parser_.services_.vec.size());
}
std::unique_ptr<const grpc_generator::Service> service(int i) const {
return std::unique_ptr<const grpc_generator::Service>(
new FlatBufService(parser_.services_.vec[i]));
}
std::unique_ptr<grpc_generator::Printer> CreatePrinter(
std::string *str, const char indentation_type = ' ') const {
return std::unique_ptr<grpc_generator::Printer>(
new FlatBufPrinter(str, indentation_type));
}
private:
const Parser &parser_;
const std::string &file_name_;
const Language language_;
};
class GoGRPCGenerator : public flatbuffers::BaseGenerator {
public:
GoGRPCGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
parser_(parser),
path_(path),
file_name_(file_name) {}
bool generate() {
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
grpc_go_generator::Parameters p;
p.custom_method_io_type = "flatbuffers.Builder";
for (int i = 0; i < file.service_count(); i++) {
auto service = file.service(i);
const Definition *def = parser_.services_.vec[i];
p.package_name = LastNamespacePart(*(def->defined_namespace));
p.service_prefix =
def->defined_namespace->GetFullyQualifiedName(""); // file.package();
std::string output =
grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
std::string filename =
NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
}
return true;
}
protected:
const Parser &parser_;
const std::string &path_, &file_name_;
};
bool GenerateGoGRPC(const Parser &parser, const std::string &path,
const std::string &file_name) {
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
return GoGRPCGenerator(parser, path, file_name).generate();
}
bool GenerateCppGRPC(const Parser &parser, const std::string &path,
const std::string &file_name) {
const auto &opts = parser.opts;
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
std::string suffix = "";
suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix;
suffix += ".";
suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension;
grpc_cpp_generator::Parameters generator_parameters;
// TODO(wvo): make the other parameters in this struct configurable.
generator_parameters.use_system_headers = true;
generator_parameters.message_header_extension = suffix;
FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
std::string header_code =
grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
std::string source_code =
grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
header_code, false) &&
flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
source_code, false);
}
class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
public:
JavaGRPCGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
bool generate() {
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
grpc_java_generator::Parameters p;
for (int i = 0; i < file.service_count(); i++) {
auto service = file.service(i);
const Definition *def = parser_.services_.vec[i];
p.package_name =
def->defined_namespace->GetFullyQualifiedName(""); // file.package();
std::string output =
grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
std::string filename =
NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
}
return true;
}
};
bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
const std::string &file_name) {
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
return JavaGRPCGenerator(parser, path, file_name).generate();
}
class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
private:
CodeWriter code_;
public:
PythonGRPCGenerator(const Parser &parser, const std::string &filename)
: BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
bool generate() {
code_.Clear();
code_ +=
"# Generated by the gRPC Python protocol compiler plugin. "
"DO NOT EDIT!\n";
code_ += "import grpc\n";
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
for (int i = 0; i < file.service_count(); i++) {
auto service = file.service(i);
code_ += grpc_python_generator::Generate(&file, service.get());
}
const auto final_code = code_.ToString();
const auto filename = GenerateFileName();
return SaveFile(filename.c_str(), final_code, false);
}
std::string GenerateFileName() {
std::string namespace_dir;
auto &namespaces = parser_.namespaces_.back()->components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (it != namespaces.begin()) namespace_dir += kPathSeparator;
namespace_dir += *it;
}
std::string grpc_py_filename = namespace_dir;
if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator;
return grpc_py_filename + file_name_ + "_grpc_fb.py";
}
};
bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
const std::string &file_name) {
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
return PythonGRPCGenerator(parser, file_name).generate();
}
class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
private:
CodeWriter code_;
public:
SwiftGRPCGenerator(const Parser &parser, const std::string &path,
const std::string &filename)
: BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
bool generate() {
code_.Clear();
code_ += "// Generated GRPC code for FlatBuffers swift!";
code_ += grpc_swift_generator::GenerateHeader();
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
for (int i = 0; i < file.service_count(); i++) {
auto service = file.service(i);
code_ += grpc_swift_generator::Generate(&file, service.get());
}
const auto final_code = code_.ToString();
const auto filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), final_code, false);
}
static std::string GeneratedFileName(const std::string &path,
const std::string &file_name) {
return path + file_name + ".grpc.swift";
}
};
bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
const std::string &file_name) {
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
return SwiftGRPCGenerator(parser, path, file_name).generate();
}
class TSGRPCGenerator : public flatbuffers::BaseGenerator {
private:
CodeWriter code_;
public:
TSGRPCGenerator(const Parser &parser, const std::string &path,
const std::string &filename)
: BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
bool generate() {
code_.Clear();
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
for (int i = 0; i < file.service_count(); i++) {
auto service = file.service(i);
code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
const auto ts_name = GeneratedFileName(path_, file_name_);
if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
code_.Clear();
code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
file_name_);
const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
return false;
}
return true;
}
static std::string GeneratedFileName(const std::string &path,
const std::string &file_name,
const bool is_interface = false) {
if (is_interface) return path + file_name + "_grpc.d.ts";
return path + file_name + "_grpc.js";
}
};
bool GenerateTSGRPC(const Parser &parser, const std::string &path,
const std::string &file_name) {
int nservices = 0;
for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
++it) {
if (!(*it)->generated) nservices++;
}
if (!nservices) return true;
return TSGRPCGenerator(parser, path, file_name).generate();
}
} // namespace flatbuffers
#if defined(_MSC_VER)
# pragma warning(pop)
#endif