From b395359b6ef8613b3b156f12f9a9e1d924953b35 Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen <aardappel@gmail.com> Date: Wed, 25 Jan 2017 13:41:34 -0800 Subject: [PATCH] Allow the flatbuffer compiler (flatc) to be built as a library. Change-Id: I71baff427243f96be1596b01ff3405fdf39386d7 --- CMakeLists.txt | 1 + include/flatbuffers/flatc.h | 96 ++++++++++++++++ src/flatc.cpp | 220 +++++++++++++----------------------- src/flatc_main.cpp | 103 +++++++++++++++++ 4 files changed, 276 insertions(+), 144 deletions(-) create mode 100644 include/flatbuffers/flatc.h create mode 100644 src/flatc_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac39960..a5d98174 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_fbs.cpp src/idl_gen_grpc.cpp src/flatc.cpp + src/flatc_main.cpp grpc/src/compiler/schema_interface.h grpc/src/compiler/cpp_generator.h grpc/src/compiler/cpp_generator.cc diff --git a/include/flatbuffers/flatc.h b/include/flatbuffers/flatc.h new file mode 100644 index 00000000..e7bc835e --- /dev/null +++ b/include/flatbuffers/flatc.h @@ -0,0 +1,96 @@ +/* + * Copyright 2017 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. + */ + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" +#include <functional> +#include <limits> +#include <string> + +#ifndef FLATC_H_ +#define FLATC_H_ + +namespace flatbuffers { + +class FlatCompiler { + public: + // Output generator for the various programming languages and formats we + // support. + struct Generator { + typedef bool (*GenerateFn)(const flatbuffers::Parser &parser, + const std::string &path, + const std::string &file_name); + typedef std::string (*MakeRuleFn)(const flatbuffers::Parser &parser, + const std::string &path, + const std::string &file_name); + + GenerateFn generate; + const char *generator_opt_short; + const char *generator_opt_long; + const char *lang_name; + GenerateFn generateGRPC; + flatbuffers::IDLOptions::Language lang; + const char *generator_help; + MakeRuleFn make_rule; + }; + + typedef void (*WarnFn)(const FlatCompiler *flatc, + const std::string &warn, + bool show_exe_name); + + typedef void (*ErrorFn)(const FlatCompiler *flatc, + const std::string &err, + bool usage, bool show_exe_name); + + // Parameters required to initialize the FlatCompiler. + struct InitParams { + InitParams() + : generators(nullptr), + num_generators(0), + warn_fn(nullptr), + error_fn(nullptr) {} + + const Generator* generators; + size_t num_generators; + WarnFn warn_fn; + ErrorFn error_fn; + }; + + explicit FlatCompiler(const InitParams& params) : params_(params) {} + + int Compile(int argc, const char** argv); + + std::string GetUsageString(const char* program_name) const; + + private: + void ParseFile(flatbuffers::Parser &parser, + const std::string &filename, + const std::string &contents, + std::vector<const char *> &include_directories) const; + + void Warn(const std::string &warn, bool show_exe_name = true) const; + + void Error(const std::string &err, bool usage = true, + bool show_exe_name = true) const; + + InitParams params_; +}; + + +} // namespace flatbuffers + +#endif // FLATC_H_ diff --git a/src/flatc.cpp b/src/flatc.cpp index f5553e85..5d426cc7 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -14,105 +14,50 @@ * limitations under the License. */ -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" -#include "flatbuffers/util.h" -#include <limits> +#include "flatbuffers/flatc.h" #define FLATC_VERSION "1.5.0 (" __DATE__ ")" -static void Error(const std::string &err, bool usage = false, - bool show_exe_name = true); +namespace flatbuffers { -// This struct allows us to create a table of all possible output generators -// for the various programming languages and formats we support. -struct Generator { - bool (*generate)(const flatbuffers::Parser &parser, - const std::string &path, - const std::string &file_name); - const char *generator_opt_short; - const char *generator_opt_long; - const char *lang_name; - bool (*generateGRPC)(const flatbuffers::Parser &parser, - const std::string &path, - const std::string &file_name); - flatbuffers::IDLOptions::Language lang; - const char *generator_help; +void FlatCompiler::ParseFile( + flatbuffers::Parser &parser, + const std::string &filename, + const std::string &contents, + std::vector<const char *> &include_directories) const { + auto local_include_directory = flatbuffers::StripFileName(filename); + include_directories.push_back(local_include_directory.c_str()); + include_directories.push_back(nullptr); + if (!parser.Parse(contents.c_str(), &include_directories[0], + filename.c_str())) + Error(parser.error_, false, false); + include_directories.pop_back(); + include_directories.pop_back(); +} - std::string (*make_rule)(const flatbuffers::Parser &parser, - const std::string &path, - const std::string &file_name); -}; +void FlatCompiler::Warn(const std::string &warn, bool show_exe_name) const { + params_.warn_fn(this, warn, show_exe_name); +} -const Generator generators[] = { - { flatbuffers::GenerateBinary, "-b", "--binary", "binary", - nullptr, - flatbuffers::IDLOptions::kBinary, - "Generate wire format binaries for any data definitions", - flatbuffers::BinaryMakeRule }, - { flatbuffers::GenerateTextFile, "-t", "--json", "text", - nullptr, - flatbuffers::IDLOptions::kJson, - "Generate text output for any data definitions", - flatbuffers::TextMakeRule }, - { flatbuffers::GenerateCPP, "-c", "--cpp", "C++", - flatbuffers::GenerateCppGRPC, - flatbuffers::IDLOptions::kCpp, - "Generate C++ headers for tables/structs", - flatbuffers::CPPMakeRule }, - { flatbuffers::GenerateGo, "-g", "--go", "Go", - flatbuffers::GenerateGoGRPC, - flatbuffers::IDLOptions::kGo, - "Generate Go files for tables/structs", - flatbuffers::GeneralMakeRule }, - { flatbuffers::GenerateGeneral, "-j", "--java", "Java", - nullptr, - flatbuffers::IDLOptions::kJava, - "Generate Java classes for tables/structs", - flatbuffers::GeneralMakeRule }, - { flatbuffers::GenerateJS, "-s", "--js", "JavaScript", - nullptr, - flatbuffers::IDLOptions::kJs, - "Generate JavaScript code for tables/structs", - flatbuffers::JSMakeRule }, - { flatbuffers::GenerateGeneral, "-n", "--csharp", "C#", - nullptr, - flatbuffers::IDLOptions::kCSharp, - "Generate C# classes for tables/structs", - flatbuffers::GeneralMakeRule }, - { flatbuffers::GeneratePython, "-p", "--python", "Python", - nullptr, - flatbuffers::IDLOptions::kPython, - "Generate Python files for tables/structs", - flatbuffers::GeneralMakeRule }, - { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", - nullptr, - flatbuffers::IDLOptions::kPhp, - "Generate PHP files for tables/structs", - flatbuffers::GeneralMakeRule }, -}; +void FlatCompiler::Error(const std::string &err, bool usage, + bool show_exe_name) const { + params_.error_fn(this, err, usage, show_exe_name); +} -const char *g_program_name = nullptr; -flatbuffers::Parser *g_parser = nullptr; +std::string FlatCompiler::GetUsageString(const char* program_name) const { + std::stringstream ss; + ss << "Usageaa: " << program_name << " [OPTION]... FILE... [-- FILE...]\n"; + for (size_t i = 0; i < params_.num_generators; ++i) { + const Generator& g = params_.generators[i]; -static void Warn(const std::string &warn, bool show_exe_name = true) { - if (show_exe_name) printf("%s: ", g_program_name); - printf("warning: %s\n", warn.c_str()); -} + std::stringstream full_name; + full_name << std::setw(12) << std::left << g.generator_opt_long; + const char *name = g.generator_opt_short ? g.generator_opt_short : " "; + const char *help = g.generator_help; -static void Error(const std::string &err, bool usage, bool show_exe_name) { - if (show_exe_name) printf("%s: ", g_program_name); - printf("error: %s\n", err.c_str()); - if (usage) { - printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", g_program_name); - for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i) - printf(" %-12s %s %s.\n", - generators[i].generator_opt_long, - generators[i].generator_opt_short - ? generators[i].generator_opt_short - : " ", - generators[i].generator_help); - printf( + ss << " " << full_name.str() << " " << name << " " << help << ".\n"; + } + ss << " -o PATH Prefix PATH to all generated files.\n" " -I PATH Search for includes in the specified path.\n" " -M Print make rules for generated files.\n" @@ -154,32 +99,18 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) { "FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" "and written to the current directory or the path given by -o.\n" - "example: %s -c -b schema1.fbs schema2.fbs data.json\n", - g_program_name); - } - if (g_parser) delete g_parser; - exit(1); + "example: " << program_name << " -c -b schema1.fbs schema2.fbs data.json\n"; + return ss.str(); } -static void ParseFile(flatbuffers::Parser &parser, const std::string &filename, - const std::string &contents, - std::vector<const char *> &include_directories) { - auto local_include_directory = flatbuffers::StripFileName(filename); - include_directories.push_back(local_include_directory.c_str()); - include_directories.push_back(nullptr); - if (!parser.Parse(contents.c_str(), &include_directories[0], - filename.c_str())) - Error(parser.error_, false, false); - include_directories.pop_back(); - include_directories.pop_back(); -} +int FlatCompiler::Compile(int argc, const char** argv) { + if (params_.generators == nullptr || params_.num_generators == 0) { + return 0; + } -int main(int argc, const char *argv[]) { - g_program_name = argv[0]; flatbuffers::IDLOptions opts; std::string output_path; - const size_t num_generators = sizeof(generators) / sizeof(generators[0]); - bool generator_enabled[num_generators] = { false }; + bool any_generator = false; bool print_make_rules = false; bool raw_binary = false; @@ -188,9 +119,11 @@ int main(int argc, const char *argv[]) { std::vector<std::string> filenames; std::vector<const char *> include_directories; std::vector<const char *> conform_include_directories; + std::vector<bool> generator_enabled(params_.num_generators, false); size_t binary_files_from = std::numeric_limits<size_t>::max(); std::string conform_to_schema; - for (int argi = 1; argi < argc; argi++) { + + for (int argi = 0; argi < argc; argi++) { std::string arg = argv[argi]; if (arg[0] == '-') { if (filenames.size() && arg[1] != '-') @@ -261,13 +194,13 @@ int main(int argc, const char *argv[]) { } else if(arg == "--grpc") { grpc_enabled = true; } else { - for (size_t i = 0; i < num_generators; ++i) { - if (arg == generators[i].generator_opt_long || - (generators[i].generator_opt_short && - arg == generators[i].generator_opt_short)) { + for (size_t i = 0; i < params_.num_generators; ++i) { + if (arg == params_.generators[i].generator_opt_long || + (params_.generators[i].generator_opt_short && + arg == params_.generators[i].generator_opt_short)) { generator_enabled[i] = true; any_generator = true; - opts.lang_to_generate |= generators[i].lang; + opts.lang_to_generate |= params_.generators[i].lang; goto found; } } @@ -297,8 +230,8 @@ int main(int argc, const char *argv[]) { conform_include_directories); } - // Now process the files: - g_parser = new flatbuffers::Parser(opts); + std::unique_ptr<flatbuffers::Parser> parser(new flatbuffers::Parser(opts)); + for (auto file_it = filenames.begin(); file_it != filenames.end(); ++file_it) { @@ -309,8 +242,8 @@ int main(int argc, const char *argv[]) { bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >= binary_files_from; if (is_binary) { - g_parser->builder_.Clear(); - g_parser->builder_.PushFlatBuffer( + parser->builder_.Clear(); + parser->builder_.PushFlatBuffer( reinterpret_cast<const uint8_t *>(contents.c_str()), contents.length()); if (!raw_binary) { @@ -319,17 +252,17 @@ int main(int argc, const char *argv[]) { // does not contain a file identifier. // We'd expect that typically any binary used as a file would have // such an identifier, so by default we require them to match. - if (!g_parser->file_identifier_.length()) { + if (!parser->file_identifier_.length()) { Error("current schema has no file_identifier: cannot test if \"" + *file_it + "\" matches the schema, use --raw-binary to read this file" " anyway."); } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), - g_parser->file_identifier_.c_str())) { + parser->file_identifier_.c_str())) { Error("binary \"" + *file_it + "\" does not have expected file_identifier \"" + - g_parser->file_identifier_ + + parser->file_identifier_ + "\", use --raw-binary to read this file anyway."); } } @@ -343,63 +276,62 @@ int main(int argc, const char *argv[]) { // If we're processing multiple schemas, make sure to start each // one from scratch. If it depends on previous schemas it must do // so explicitly using an include. - delete g_parser; - g_parser = new flatbuffers::Parser(opts); + parser.reset(new flatbuffers::Parser(opts)); } - ParseFile(*g_parser, *file_it, contents, include_directories); + ParseFile(*parser.get(), *file_it, contents, include_directories); if (is_schema && !conform_to_schema.empty()) { - auto err = g_parser->ConformTo(conform_parser); + auto err = parser->ConformTo(conform_parser); if (!err.empty()) Error("schemas don\'t conform: " + err); } if (schema_binary) { - g_parser->Serialize(); - g_parser->file_extension_ = reflection::SchemaExtension(); + parser->Serialize(); + parser->file_extension_ = reflection::SchemaExtension(); } } std::string filebase = flatbuffers::StripPath( flatbuffers::StripExtension(*file_it)); - for (size_t i = 0; i < num_generators; ++i) { - g_parser->opts.lang = generators[i].lang; + for (size_t i = 0; i < params_.num_generators; ++i) { + parser->opts.lang = params_.generators[i].lang; if (generator_enabled[i]) { if (!print_make_rules) { flatbuffers::EnsureDirExists(output_path); - if (!generators[i].generate(*g_parser, output_path, filebase)) { + if (!params_.generators[i].generate(*parser.get(), output_path, filebase)) { Error(std::string("Unable to generate ") + - generators[i].lang_name + + params_.generators[i].lang_name + " for " + filebase); } } else { - std::string make_rule = generators[i].make_rule( - *g_parser, output_path, *file_it); + std::string make_rule = params_.generators[i].make_rule( + *parser.get(), output_path, *file_it); if (!make_rule.empty()) printf("%s\n", flatbuffers::WordWrap( make_rule, 80, " ", " \\").c_str()); } if (grpc_enabled) { - if (generators[i].generateGRPC != nullptr) { - if (!generators[i].generateGRPC(*g_parser, output_path, + if (params_.generators[i].generateGRPC != nullptr) { + if (!params_.generators[i].generateGRPC(*parser.get(), output_path, filebase)) { Error(std::string("Unable to generate GRPC interface for") + - generators[i].lang_name); + params_.generators[i].lang_name); } } else { Warn(std::string("GRPC interface generator not implemented for ") - + generators[i].lang_name); + + params_.generators[i].lang_name); } } } } - if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase); + if (opts.proto_mode) GenerateFBS(*parser.get(), output_path, filebase); // We do not want to generate code for the definitions in this file // in any files coming up next. - g_parser->MarkGenerated(); + parser->MarkGenerated(); } - - delete g_parser; return 0; } + +} // namespace flatbuffers diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp new file mode 100644 index 00000000..2063731c --- /dev/null +++ b/src/flatc_main.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2017 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. + */ + +#include "flatbuffers/flatc.h" + +static const char *g_program_name = nullptr; + +static void Warn(const flatbuffers::FlatCompiler* flatc, + const std::string &warn, + bool show_exe_name) { + if (show_exe_name) { + printf("%s: ", g_program_name); + } + printf("warning: %s\n", warn.c_str()); +} + +static void Error(const flatbuffers::FlatCompiler* flatc, + const std::string &err, + bool usage, + bool show_exe_name) { + if (show_exe_name) { + printf("%s: ", g_program_name); + } + printf("error: %s\n", err.c_str()); + if (usage) { + printf("%s", flatc->GetUsageString(g_program_name).c_str()); + } + exit(1); +} + +int main(int argc, const char *argv[]) { + g_program_name = argv[0]; + + const flatbuffers::FlatCompiler::Generator generators[] = { + { flatbuffers::GenerateBinary, "-b", "--binary", "binary", + nullptr, + flatbuffers::IDLOptions::kBinary, + "Generate wire format binaries for any data definitions", + flatbuffers::BinaryMakeRule }, + { flatbuffers::GenerateTextFile, "-t", "--json", "text", + nullptr, + flatbuffers::IDLOptions::kJson, + "Generate text output for any data definitions", + flatbuffers::TextMakeRule }, + { flatbuffers::GenerateCPP, "-c", "--cpp", "C++", + flatbuffers::GenerateCppGRPC, + flatbuffers::IDLOptions::kCpp, + "Generate C++ headers for tables/structs", + flatbuffers::CPPMakeRule }, + { flatbuffers::GenerateGo, "-g", "--go", "Go", + flatbuffers::GenerateGoGRPC, + flatbuffers::IDLOptions::kGo, + "Generate Go files for tables/structs", + flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateGeneral, "-j", "--java", "Java", + nullptr, + flatbuffers::IDLOptions::kJava, + "Generate Java classes for tables/structs", + flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateJS, "-s", "--js", "JavaScript", + nullptr, + flatbuffers::IDLOptions::kJs, + "Generate JavaScript code for tables/structs", + flatbuffers::JSMakeRule }, + { flatbuffers::GenerateGeneral, "-n", "--csharp", "C#", + nullptr, + flatbuffers::IDLOptions::kCSharp, + "Generate C# classes for tables/structs", + flatbuffers::GeneralMakeRule }, + { flatbuffers::GeneratePython, "-p", "--python", "Python", + nullptr, + flatbuffers::IDLOptions::kPython, + "Generate Python files for tables/structs", + flatbuffers::GeneralMakeRule }, + { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", + nullptr, + flatbuffers::IDLOptions::kPhp, + "Generate PHP files for tables/structs", + flatbuffers::GeneralMakeRule }, + }; + + flatbuffers::FlatCompiler::InitParams params; + params.generators = generators; + params.num_generators = sizeof(generators) / sizeof(generators[0]); + params.warn_fn = Warn; + params.error_fn = Error; + + flatbuffers::FlatCompiler flatc(params); + return flatc.Compile(argc - 1, argv + 1); +} -- GitLab