flatc.cpp revision eba6b6f7c93cab4b945f1e39d9ef413d51d3711d
1/* 2 * Copyright 2014 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "flatbuffers/flatbuffers.h" 18#include "flatbuffers/idl.h" 19#include "flatbuffers/util.h" 20#include <limits> 21 22#define FLATC_VERSION "1.4.0 (" __DATE__ ")" 23 24static void Error(const std::string &err, bool usage = false, 25 bool show_exe_name = true); 26 27// This struct allows us to create a table of all possible output generators 28// for the various programming languages and formats we support. 29struct Generator { 30 bool (*generate)(const flatbuffers::Parser &parser, 31 const std::string &path, 32 const std::string &file_name); 33 const char *generator_opt_short; 34 const char *generator_opt_long; 35 const char *lang_name; 36 flatbuffers::IDLOptions::Language lang; 37 const char *generator_help; 38 39 std::string (*make_rule)(const flatbuffers::Parser &parser, 40 const std::string &path, 41 const std::string &file_name); 42}; 43 44const Generator generators[] = { 45 { flatbuffers::GenerateBinary, "-b", "--binary", "binary", 46 flatbuffers::IDLOptions::kMAX, 47 "Generate wire format binaries for any data definitions", 48 flatbuffers::BinaryMakeRule }, 49 { flatbuffers::GenerateTextFile, "-t", "--json", "text", 50 flatbuffers::IDLOptions::kMAX, 51 "Generate text output for any data definitions", 52 flatbuffers::TextMakeRule }, 53 { flatbuffers::GenerateCPP, "-c", "--cpp", "C++", 54 flatbuffers::IDLOptions::kMAX, 55 "Generate C++ headers for tables/structs", 56 flatbuffers::CPPMakeRule }, 57 { flatbuffers::GenerateGo, "-g", "--go", "Go", 58 flatbuffers::IDLOptions::kGo, 59 "Generate Go files for tables/structs", 60 flatbuffers::GeneralMakeRule }, 61 { flatbuffers::GenerateGeneral, "-j", "--java", "Java", 62 flatbuffers::IDLOptions::kJava, 63 "Generate Java classes for tables/structs", 64 flatbuffers::GeneralMakeRule }, 65 { flatbuffers::GenerateJS, "-s", "--js", "JavaScript", 66 flatbuffers::IDLOptions::kMAX, 67 "Generate JavaScript code for tables/structs", 68 flatbuffers::JSMakeRule }, 69 { flatbuffers::GenerateGeneral, "-n", "--csharp", "C#", 70 flatbuffers::IDLOptions::kCSharp, 71 "Generate C# classes for tables/structs", 72 flatbuffers::GeneralMakeRule }, 73 { flatbuffers::GeneratePython, "-p", "--python", "Python", 74 flatbuffers::IDLOptions::kMAX, 75 "Generate Python files for tables/structs", 76 flatbuffers::GeneralMakeRule }, 77 { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", 78 flatbuffers::IDLOptions::kMAX, 79 "Generate PHP files for tables/structs", 80 flatbuffers::GeneralMakeRule }, 81 { flatbuffers::GenerateGRPC, nullptr, "--grpc", "GRPC", 82 flatbuffers::IDLOptions::kMAX, 83 "Generate GRPC interfaces", 84 flatbuffers::CPPMakeRule }, 85}; 86 87const char *g_program_name = nullptr; 88flatbuffers::Parser *g_parser = nullptr; 89 90static void Error(const std::string &err, bool usage, bool show_exe_name) { 91 if (show_exe_name) printf("%s: ", g_program_name); 92 printf("%s\n", err.c_str()); 93 if (usage) { 94 printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", g_program_name); 95 for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i) 96 printf(" %-12s %s %s.\n", 97 generators[i].generator_opt_long, 98 generators[i].generator_opt_short 99 ? generators[i].generator_opt_short 100 : " ", 101 generators[i].generator_help); 102 printf( 103 " -o PATH Prefix PATH to all generated files.\n" 104 " -I PATH Search for includes in the specified path.\n" 105 " -M Print make rules for generated files.\n" 106 " --version Print the version number of flatc and exit.\n" 107 " --strict-json Strict JSON: field names must be / will be quoted,\n" 108 " no trailing commas in tables/vectors.\n" 109 " --allow-non-utf8 Pass non-UTF-8 input through parser and emit nonstandard\n" 110 " \\x escapes in JSON. (Default is to raise parse error on\n" 111 " non-UTF-8 input.)\n" 112 " --defaults-json Output fields whose value is the default when\n" 113 " writing JSON\n" 114 " --unknown-json Allow fields in JSON that are not defined in the\n" 115 " schema. These fields will be discared when generating\n" 116 " binaries.\n" 117 " --no-prefix Don\'t prefix enum values with the enum type in C++.\n" 118 " --scoped-enums Use C++11 style scoped and strongly typed enums.\n" 119 " also implies --no-prefix.\n" 120 " --gen-includes (deprecated), this is the default behavior.\n" 121 " If the original behavior is required (no include\n" 122 " statements) use --no-includes.\n" 123 " --no-includes Don\'t generate include statements for included\n" 124 " schemas the generated file depends on (C++).\n" 125 " --gen-mutable Generate accessors that can mutate buffers in-place.\n" 126 " --gen-onefile Generate single output file for C#.\n" 127 " --gen-name-strings Generate type name functions for C++.\n" 128 " --escape-proto-ids Disable appending '_' in namespaces names.\n" 129 " --gen-object-api Generate an additional object-based API\n" 130 " --raw-binary Allow binaries without file_indentifier to be read.\n" 131 " This may crash flatc given a mismatched schema.\n" 132 " --proto Input is a .proto, translate to .fbs.\n" 133 " --schema Serialize schemas instead of JSON (use with -b)\n" 134 " --conform FILE Specify a schema the following schemas should be\n" 135 " an evolution of. Gives errors if not.\n" 136 "FILEs may be schemas, or JSON files (conforming to preceding schema)\n" 137 "FILEs after the -- must be binary flatbuffer format files.\n" 138 "Output files are named using the base file name of the input,\n" 139 "and written to the current directory or the path given by -o.\n" 140 "example: %s -c -b schema1.fbs schema2.fbs data.json\n", 141 g_program_name); 142 } 143 if (g_parser) delete g_parser; 144 exit(1); 145} 146 147static void ParseFile(flatbuffers::Parser &parser, const std::string &filename, 148 const std::string &contents, 149 std::vector<const char *> &include_directories) { 150 auto local_include_directory = flatbuffers::StripFileName(filename); 151 include_directories.push_back(local_include_directory.c_str()); 152 include_directories.push_back(nullptr); 153 if (!parser.Parse(contents.c_str(), &include_directories[0], 154 filename.c_str())) 155 Error(parser.error_, false, false); 156 include_directories.pop_back(); 157 include_directories.pop_back(); 158} 159 160int main(int argc, const char *argv[]) { 161 g_program_name = argv[0]; 162 flatbuffers::IDLOptions opts; 163 std::string output_path; 164 const size_t num_generators = sizeof(generators) / sizeof(generators[0]); 165 bool generator_enabled[num_generators] = { false }; 166 bool any_generator = false; 167 bool print_make_rules = false; 168 bool raw_binary = false; 169 bool schema_binary = false; 170 std::vector<std::string> filenames; 171 std::vector<const char *> include_directories; 172 size_t binary_files_from = std::numeric_limits<size_t>::max(); 173 std::string conform_to_schema; 174 for (int argi = 1; argi < argc; argi++) { 175 std::string arg = argv[argi]; 176 if (arg[0] == '-') { 177 if (filenames.size() && arg[1] != '-') 178 Error("invalid option location: " + arg, true); 179 if (arg == "-o") { 180 if (++argi >= argc) Error("missing path following: " + arg, true); 181 output_path = flatbuffers::ConCatPathFileName(argv[argi], ""); 182 } else if(arg == "-I") { 183 if (++argi >= argc) Error("missing path following" + arg, true); 184 include_directories.push_back(argv[argi]); 185 } else if(arg == "--conform") { 186 if (++argi >= argc) Error("missing path following" + arg, true); 187 conform_to_schema = argv[argi]; 188 } else if(arg == "--strict-json") { 189 opts.strict_json = true; 190 } else if(arg == "--allow-non-utf8") { 191 opts.allow_non_utf8 = true; 192 } else if(arg == "--no-js-exports") { 193 opts.skip_js_exports = true; 194 } else if(arg == "--defaults-json") { 195 opts.output_default_scalars_in_json = true; 196 } else if (arg == "--unknown-json") { 197 opts.skip_unexpected_fields_in_json = true; 198 } else if(arg == "--no-prefix") { 199 opts.prefixed_enums = false; 200 } else if(arg == "--scoped-enums") { 201 opts.prefixed_enums = false; 202 opts.scoped_enums = true; 203 } else if (arg == "--no-union-value-namespacing") { 204 opts.union_value_namespacing = false; 205 } else if(arg == "--gen-mutable") { 206 opts.mutable_buffer = true; 207 } else if(arg == "--gen-name-strings") { 208 opts.generate_name_strings = true; 209 } else if(arg == "--gen-object-api") { 210 opts.generate_object_based_api = true; 211 } else if(arg == "--gen-all") { 212 opts.generate_all = true; 213 opts.include_dependence_headers = false; 214 } else if(arg == "--gen-includes") { 215 // Deprecated, remove this option some time in the future. 216 printf("warning: --gen-includes is deprecated (it is now default)\n"); 217 } else if(arg == "--no-includes") { 218 opts.include_dependence_headers = false; 219 } else if (arg == "--gen-onefile") { 220 opts.one_file = true; 221 } else if (arg == "--raw-binary") { 222 raw_binary = true; 223 } else if(arg == "--") { // Separator between text and binary inputs. 224 binary_files_from = filenames.size(); 225 } else if(arg == "--proto") { 226 opts.proto_mode = true; 227 } else if(arg == "--escape-proto-ids") { 228 opts.escape_proto_identifiers = true; 229 } else if(arg == "--schema") { 230 schema_binary = true; 231 } else if(arg == "-M") { 232 print_make_rules = true; 233 } else if(arg == "--version") { 234 printf("flatc version %s\n", FLATC_VERSION); 235 exit(0); 236 } else { 237 for (size_t i = 0; i < num_generators; ++i) { 238 if (arg == generators[i].generator_opt_long || 239 (generators[i].generator_opt_short && 240 arg == generators[i].generator_opt_short)) { 241 generator_enabled[i] = true; 242 any_generator = true; 243 goto found; 244 } 245 } 246 Error("unknown commandline argument" + arg, true); 247 found:; 248 } 249 } else { 250 filenames.push_back(argv[argi]); 251 } 252 } 253 254 if (!filenames.size()) Error("missing input files", false, true); 255 256 if (opts.proto_mode) { 257 if (any_generator) 258 Error("cannot generate code directly from .proto files", true); 259 } else if (!any_generator && conform_to_schema.empty()) { 260 Error("no options: specify at least one generator.", true); 261 } 262 263 flatbuffers::Parser conform_parser; 264 if (!conform_to_schema.empty()) { 265 std::string contents; 266 if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) 267 Error("unable to load schema: " + conform_to_schema); 268 ParseFile(conform_parser, conform_to_schema, contents, include_directories); 269 } 270 271 // Now process the files: 272 g_parser = new flatbuffers::Parser(opts); 273 for (auto file_it = filenames.begin(); 274 file_it != filenames.end(); 275 ++file_it) { 276 std::string contents; 277 if (!flatbuffers::LoadFile(file_it->c_str(), true, &contents)) 278 Error("unable to load file: " + *file_it); 279 280 bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >= 281 binary_files_from; 282 if (is_binary) { 283 g_parser->builder_.Clear(); 284 g_parser->builder_.PushFlatBuffer( 285 reinterpret_cast<const uint8_t *>(contents.c_str()), 286 contents.length()); 287 if (!raw_binary) { 288 // Generally reading binaries that do not correspond to the schema 289 // will crash, and sadly there's no way around that when the binary 290 // does not contain a file identifier. 291 // We'd expect that typically any binary used as a file would have 292 // such an identifier, so by default we require them to match. 293 if (!g_parser->file_identifier_.length()) { 294 Error("current schema has no file_identifier: cannot test if \"" + 295 *file_it + 296 "\" matches the schema, use --raw-binary to read this file" 297 " anyway."); 298 } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), 299 g_parser->file_identifier_.c_str())) { 300 Error("binary \"" + 301 *file_it + 302 "\" does not have expected file_identifier \"" + 303 g_parser->file_identifier_ + 304 "\", use --raw-binary to read this file anyway."); 305 } 306 } 307 } else { 308 // Check if file contains 0 bytes. 309 if (contents.length() != strlen(contents.c_str())) { 310 Error("input file appears to be binary: " + *file_it, true); 311 } 312 auto is_schema = flatbuffers::GetExtension(*file_it) == "fbs"; 313 if (is_schema) { 314 // If we're processing multiple schemas, make sure to start each 315 // one from scratch. If it depends on previous schemas it must do 316 // so explicitly using an include. 317 delete g_parser; 318 g_parser = new flatbuffers::Parser(opts); 319 } 320 ParseFile(*g_parser, *file_it, contents, include_directories); 321 if (is_schema && !conform_to_schema.empty()) { 322 auto err = g_parser->ConformTo(conform_parser); 323 if (!err.empty()) Error("schemas don\'t conform: " + err); 324 } 325 if (schema_binary) { 326 g_parser->Serialize(); 327 g_parser->file_extension_ = reflection::SchemaExtension(); 328 } 329 } 330 331 std::string filebase = flatbuffers::StripPath( 332 flatbuffers::StripExtension(*file_it)); 333 334 for (size_t i = 0; i < num_generators; ++i) { 335 g_parser->opts.lang = generators[i].lang; 336 if (generator_enabled[i]) { 337 if (!print_make_rules) { 338 flatbuffers::EnsureDirExists(output_path); 339 if (!generators[i].generate(*g_parser, output_path, filebase)) { 340 Error(std::string("Unable to generate ") + 341 generators[i].lang_name + 342 " for " + 343 filebase); 344 } 345 } else { 346 std::string make_rule = generators[i].make_rule( 347 *g_parser, output_path, *file_it); 348 if (!make_rule.empty()) 349 printf("%s\n", flatbuffers::WordWrap( 350 make_rule, 80, " ", " \\").c_str()); 351 } 352 } 353 } 354 355 if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase); 356 357 // We do not want to generate code for the definitions in this file 358 // in any files coming up next. 359 g_parser->MarkGenerated(); 360 } 361 362 delete g_parser; 363 return 0; 364} 365