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