#include <map>
#include <set>
+#include <stack>
#include <memory>
#include <functional>
Definition() : generated(false), defined_namespace(nullptr) {}
std::string name;
+ std::string file;
std::vector<std::string> doc_comment;
SymbolTable<Value> attributes;
bool generated; // did we already output code for this definition?
// Mark all definitions as already having code generated.
void MarkGenerated();
+ // Get the files recursively included by the given file. The returned
+ // container will have at least the given file.
+ std::set<std::string> GetIncludedFilesRecursive(
+ const std::string &file_name) const;
+
private:
int64_t ParseHexNum(int nibbles);
void Next();
std::string file_extension_;
std::map<std::string, bool> included_files_;
+ std::map<std::string, std::set<std::string>> files_included_per_file_;
private:
const char *source_, *cursor_;
int line_; // the current line being parsed
int token_;
+ std::stack<std::string> files_being_parsed_;
bool proto_mode_;
bool strict_json_;
std::string attribute_;
bool include_dependence_headers;
// Possible options for the more general generator below.
- enum Language { kJava, kCSharp, kMAX };
+ enum Language { kJava, kCSharp, kGo, kMAX };
Language lang;
const void *flatbuffer,
const GeneratorOptions &opts,
std::string *text);
+extern bool GenerateTextFile(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
+
+// Generate binary files from a given FlatBuffer, and a given Parser
+// object that has been populated with the corresponding schema.
+// See idl_gen_general.cpp.
+extern bool GenerateBinary(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
// Generate a C++ header from the definitions in the Parser object.
// See idl_gen_cpp.
const std::string &file_name,
const GeneratorOptions &opts);
+// Generate a make rule for the generated C++ header.
+// See idl_gen_cpp.cpp.
+extern std::string CPPMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
+
+// Generate a make rule for the generated Java/C#/... files.
+// See idl_gen_general.cpp.
+extern std::string GeneralMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
+
+// Generate a make rule for the generated text (JSON) files.
+// See idl_gen_text.cpp.
+extern std::string TextMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
+
+// Generate a make rule for the generated binary files.
+// See idl_gen_general.cpp.
+extern std::string BinaryMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts);
+
} // namespace flatbuffers
#endif // FLATBUFFERS_IDL_H_
#endif
}
+// Check if file "name" exists.
+inline bool FileExists(const char *name) {
+ std::ifstream ifs(name);
+ return ifs.good();
+}
+
// Load file "name" into "buf" returning true if successful
// false otherwise. If "binary" is false data is read
// using ifstream's text mode, otherwise data is read with
return ucc;
}
+// Wraps a string to a maximum length, inserting new lines where necessary. Any
+// existing whitespace will be collapsed down to a single space. A prefix or
+// suffix can be provided, which will be inserted before or after a wrapped
+// line, respectively.
+inline std::string WordWrap(const std::string in, size_t max_length,
+ const std::string wrapped_line_prefix,
+ const std::string wrapped_line_suffix) {
+ std::istringstream in_stream(in);
+ std::string wrapped, line, word;
+
+ in_stream >> word;
+ line = word;
+
+ while (in_stream >> word) {
+ if ((line.length() + 1 + word.length() + wrapped_line_suffix.length()) <
+ max_length) {
+ line += " " + word;
+ } else {
+ wrapped += line + wrapped_line_suffix + "\n";
+ line = wrapped_line_prefix + word;
+ }
+ }
+ wrapped += line;
+
+ return wrapped;
+}
+
} // namespace flatbuffers
#endif // FLATBUFFERS_UTIL_H_
static void Error(const char *err, const char *obj = nullptr,
bool usage = false, bool show_exe_name = true);
-namespace flatbuffers {
-
-bool GenerateBinary(const Parser &parser,
- const std::string &path,
- const std::string &file_name,
- const GeneratorOptions & /*opts*/) {
- auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
- return !parser.builder_.GetSize() ||
- flatbuffers::SaveFile(
- (path + file_name + "." + ext).c_str(),
- reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
- parser.builder_.GetSize(),
- true);
-}
-
-bool GenerateTextFile(const Parser &parser,
- const std::string &path,
- const std::string &file_name,
- const GeneratorOptions &opts) {
- if (!parser.builder_.GetSize()) return true;
- if (!parser.root_struct_def) Error("root_type not set");
- std::string text;
- GenerateText(parser, parser.builder_.GetBufferPointer(), opts,
- &text);
- return flatbuffers::SaveFile((path + file_name + ".json").c_str(),
- text,
- false);
-
-}
-
-}
-
// This struct allows us to create a table of all possible output generators
// for the various programming languages and formats we support.
struct Generator {
const std::string &path,
const std::string &file_name,
const flatbuffers::GeneratorOptions &opts);
- const char *opt;
- const char *name;
+ const char *generator_opt;
+ const char *lang_name;
flatbuffers::GeneratorOptions::Language lang;
- const char *help;
+ const char *generator_help;
+
+ std::string (*make_rule)(const flatbuffers::Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const flatbuffers::GeneratorOptions &opts);
};
const Generator generators[] = {
{ flatbuffers::GenerateBinary, "-b", "binary",
flatbuffers::GeneratorOptions::kMAX,
- "Generate wire format binaries for any data definitions" },
+ "Generate wire format binaries for any data definitions",
+ flatbuffers::BinaryMakeRule },
{ flatbuffers::GenerateTextFile, "-t", "text",
flatbuffers::GeneratorOptions::kMAX,
- "Generate text output for any data definitions" },
+ "Generate text output for any data definitions",
+ flatbuffers::TextMakeRule },
{ flatbuffers::GenerateCPP, "-c", "C++",
flatbuffers::GeneratorOptions::kMAX,
- "Generate C++ headers for tables/structs" },
+ "Generate C++ headers for tables/structs",
+ flatbuffers::CPPMakeRule },
{ flatbuffers::GenerateGo, "-g", "Go",
- flatbuffers::GeneratorOptions::kMAX,
- "Generate Go files for tables/structs" },
+ flatbuffers::GeneratorOptions::kGo,
+ "Generate Go files for tables/structs",
+ flatbuffers::GeneralMakeRule },
{ flatbuffers::GenerateGeneral, "-j", "Java",
flatbuffers::GeneratorOptions::kJava,
- "Generate Java classes for tables/structs" },
+ "Generate Java classes for tables/structs",
+ flatbuffers::GeneralMakeRule },
{ flatbuffers::GenerateGeneral, "-n", "C#",
flatbuffers::GeneratorOptions::kCSharp,
- "Generate C# classes for tables/structs" }
+ "Generate C# classes for tables/structs",
+ flatbuffers::GeneralMakeRule },
};
const char *program_name = NULL;
if (usage) {
printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", program_name);
for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i)
- printf(" %s %s.\n", generators[i].opt, generators[i].help);
+ printf(" %s %s.\n",
+ generators[i].generator_opt,
+ generators[i].generator_help);
printf(
" -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"
" --strict-json Strict JSON: field names must be / will be quoted,\n"
" no trailing commas in tables/vectors.\n"
" --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
" --proto Input is a .proto, translate to .fbs.\n"
"FILEs may depend on declarations in earlier files.\n"
"FILEs after the -- must be binary flatbuffer format files.\n"
- "Output files are named using the base file name of the input,"
+ "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",
program_name);
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 proto_mode = false;
std::vector<std::string> filenames;
std::vector<const char *> include_directories;
} else if(opt == "--proto") {
proto_mode = true;
any_generator = true;
+ } else if(opt == "-M") {
+ print_make_rules = true;
} else {
for (size_t i = 0; i < num_generators; ++i) {
- if(opt == generators[i].opt) {
+ if (opt == generators[i].generator_opt) {
generator_enabled[i] = true;
any_generator = true;
goto found;
if (!filenames.size()) Error("missing input files", nullptr, true);
if (!any_generator)
- Error("no options: no output files generated.",
- "specify one of -c -g -j -t -b etc.", true);
+ Error("no options", "specify one of -c -g -j -t -b etc.", true);
// Now process the files:
flatbuffers::Parser parser(opts.strict_json, proto_mode);
flatbuffers::StripExtension(*file_it));
for (size_t i = 0; i < num_generators; ++i) {
+ opts.lang = generators[i].lang;
if (generator_enabled[i]) {
- flatbuffers::EnsureDirExists(output_path);
- opts.lang = generators[i].lang;
- if (!generators[i].generate(parser, output_path, filebase, opts)) {
- Error((std::string("Unable to generate ") +
- generators[i].name +
- " for " +
- filebase).c_str());
+ if (!print_make_rules) {
+ flatbuffers::EnsureDirExists(output_path);
+ if (!generators[i].generate(parser, output_path, filebase, opts)) {
+ Error((std::string("Unable to generate ") +
+ generators[i].lang_name +
+ " for " +
+ filebase).c_str());
+ }
+ } else {
+ std::string make_rule = generators[i].make_rule(
+ parser, output_path, *file_it, opts);
+ if (!make_rule.empty())
+ printf("%s\n", flatbuffers::WordWrap(
+ make_rule, 80, " ", " \\").c_str());
}
}
}
return std::string();
}
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.h";
+}
+
bool GenerateCPP(const Parser &parser,
const std::string &path,
const std::string &file_name,
const GeneratorOptions &opts) {
auto code = GenerateCPP(parser, file_name, opts);
return !code.length() ||
- SaveFile((path + file_name + "_generated.h").c_str(), code, false);
+ SaveFile(GeneratedFileName(path, file_name).c_str(), code, false);
+}
+
+std::string CPPMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions & /*opts*/) {
+ std::string filebase = flatbuffers::StripPath(
+ flatbuffers::StripExtension(file_name));
+ std::string make_rule = GeneratedFileName(path, filebase) + ": ";
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin();
+ it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
}
} // namespace flatbuffers
"\n}\n",
"",
"using FlatBuffers;\n\n",
+ },
+ // TODO: add Go support to the general generator.
+ // WARNING: this is currently only used for generating make rules for Go.
+ {
+ GeneratorOptions::kGo,
+ true,
+ ".go",
+ "string",
+ "bool ",
+ "\n{\n",
+ "const ",
+ "",
+ "package ",
+ "",
+ "",
+ "",
+ "import (\n\tflatbuffers \"github.com/google/flatbuffers/go\"\n)",
}
};
const Type &type) {
static const char *gtypename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- #JTYPE, #NTYPE,
+ #JTYPE, #NTYPE, #GTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
};
+
return gtypename[type.base_type * GeneratorOptions::kMAX + lang.language];
}
return true;
}
+static std::string ClassFileName(const LanguageParameters &lang,
+ const Parser &parser, const Definition &def,
+ const std::string &path) {
+ std::string namespace_general;
+ std::string namespace_dir = path;
+ auto &namespaces = parser.namespaces_.back()->components;
+ for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+ if (namespace_general.length()) {
+ namespace_general += ".";
+ namespace_dir += kPathSeparator;
+ }
+ namespace_general += *it;
+ namespace_dir += *it;
+ }
+
+ return namespace_dir + kPathSeparator + def.name + lang.file_extension;
+}
+
+std::string GeneralMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts) {
+ assert(opts.lang <= GeneratorOptions::kMAX);
+ auto lang = language_parameters[opts.lang];
+
+ std::string make_rule;
+
+ for (auto it = parser.enums_.vec.begin();
+ it != parser.enums_.vec.end(); ++it) {
+ if (make_rule != "")
+ make_rule += " ";
+ make_rule += ClassFileName(lang, parser, **it, path);
+ }
+
+ for (auto it = parser.structs_.vec.begin();
+ it != parser.structs_.vec.end(); ++it) {
+ if (make_rule != "")
+ make_rule += " ";
+ make_rule += ClassFileName(lang, parser, **it, path);
+ }
+
+ make_rule += ": ";
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin();
+ it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+std::string BinaryFileName(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name) {
+ auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
+ return path + file_name + "." + ext;
+}
+
+bool GenerateBinary(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions & /*opts*/) {
+ return !parser.builder_.GetSize() ||
+ flatbuffers::SaveFile(
+ BinaryFileName(parser, path, file_name).c_str(),
+ reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
+ parser.builder_.GetSize(),
+ true);
+}
+
+std::string BinaryMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions & /*opts*/) {
+ if (!parser.builder_.GetSize()) return "";
+ std::string filebase = flatbuffers::StripPath(
+ flatbuffers::StripExtension(file_name));
+ std::string make_rule = BinaryFileName(parser, path, filebase) + ": " +
+ file_name;
+ auto included_files = parser.GetIncludedFilesRecursive(
+ parser.root_struct_def->file);
+ for (auto it = included_files.begin();
+ it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
} // namespace flatbuffers
text += NewLine(opts);
}
+std::string TextFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + ".json";
+}
+
+bool GenerateTextFile(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions &opts) {
+ if (!parser.builder_.GetSize() || !parser.root_struct_def) return true;
+ std::string text;
+ GenerateText(parser, parser.builder_.GetBufferPointer(), opts,
+ &text);
+ return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
+ text,
+ false);
+}
+
+std::string TextMakeRule(const Parser &parser,
+ const std::string &path,
+ const std::string &file_name,
+ const GeneratorOptions & /*opts*/) {
+ if (!parser.builder_.GetSize() || !parser.root_struct_def) return "";
+ std::string filebase = flatbuffers::StripPath(
+ flatbuffers::StripExtension(file_name));
+ std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
+ auto included_files = parser.GetIncludedFilesRecursive(
+ parser.root_struct_def->file);
+ for (auto it = included_files.begin();
+ it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
} // namespace flatbuffers
*/
#include <algorithm>
+#include <list>
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
field.value.offset =
FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
field.name = name;
+ field.file = struct_def.file;
field.value.type = type;
if (struct_def.fixed) { // statically compute the field offset
auto size = InlineSize(type);
Expect(kTokenIdentifier);
auto &enum_def = *new EnumDef();
enum_def.name = name;
+ if (!files_being_parsed_.empty()) enum_def.file = files_being_parsed_.top();
enum_def.doc_comment = dc;
enum_def.is_union = is_union;
enum_def.defined_namespace = namespaces_.back();
if (!struct_def.predecl) Error("datatype already exists: " + name);
struct_def.predecl = false;
struct_def.name = name;
+ if (!files_being_parsed_.empty()) struct_def.file = files_being_parsed_.top();
// Move this struct to the back of the vector just in case it was predeclared,
// to preserve declaration order.
remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
bool Parser::Parse(const char *source, const char **include_paths,
const char *source_filename) {
- if (source_filename) included_files_[source_filename] = true;
+ if (source_filename &&
+ included_files_.find(source_filename) == included_files_.end()) {
+ included_files_[source_filename] = true;
+ files_included_per_file_[source_filename] = std::set<std::string>();
+ files_being_parsed_.push(source_filename);
+ }
+ if (!include_paths) {
+ const char *current_directory[] = { "", nullptr };
+ include_paths = current_directory;
+ }
source_ = cursor_ = source;
line_ = 1;
error_.clear();
while (IsNext(kTokenInclude)) {
auto name = attribute_;
Expect(kTokenStringConstant);
- if (included_files_.find(name) == included_files_.end()) {
+ // Look for the file in include_paths.
+ std::string filepath;
+ for (auto paths = include_paths; paths && *paths; paths++) {
+ filepath = flatbuffers::ConCatPathFileName(*paths, name);
+ if(FileExists(filepath.c_str())) break;
+ }
+ if (filepath.empty())
+ Error("unable to locate include file: " + name);
+ if (source_filename)
+ files_included_per_file_[source_filename].insert(filepath);
+ if (included_files_.find(filepath) == included_files_.end()) {
// We found an include file that we have not parsed yet.
// Load it and parse it.
std::string contents;
- if (!include_paths) {
- const char *current_directory[] = { "", nullptr };
- include_paths = current_directory;
- }
- for (auto paths = include_paths; paths && *paths; paths++) {
- auto filepath = flatbuffers::ConCatPathFileName(*paths, name);
- if(LoadFile(filepath.c_str(), true, &contents)) break;
- }
- if (contents.empty())
+ if (!LoadFile(filepath.c_str(), true, &contents))
Error("unable to load include file: " + name);
- included_files_[name] = true;
- if (!Parse(contents.c_str(), include_paths)) {
+ if (!Parse(contents.c_str(), include_paths, filepath.c_str())) {
// Any errors, we're done.
return false;
}
error_ += NumToString(line_) + ":0"; // gcc alike
#endif
error_ += ": error: " + msg;
+ if (source_filename) files_being_parsed_.pop();
return false;
}
+ if (source_filename) files_being_parsed_.pop();
assert(!struct_stack_.size());
return true;
}
+std::set<std::string> Parser::GetIncludedFilesRecursive(
+ const std::string &file_name) const {
+ std::set<std::string> included_files;
+ std::list<std::string> to_process;
+
+ if (file_name.empty()) return included_files;
+ to_process.push_back(file_name);
+
+ while (!to_process.empty()) {
+ std::string current = to_process.front();
+ to_process.pop_front();
+ included_files.insert(current);
+
+ auto new_files = files_included_per_file_.at(current);
+ for (auto it = new_files.begin(); it != new_files.end(); ++it) {
+ if (included_files.find(*it) == included_files.end())
+ to_process.push_back(*it);
+ }
+ }
+
+ return included_files;
+}
+
} // namespace flatbuffers