flatc.cpp revision d70f5ac6b02f250dca8e2c6e8f59a4223d1f66f6
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.3.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 " --defaults-json Output fields whose value is the default when\n" 110 " writing JSON\n" 111 " --unknown-json Allow fields in JSON that are not defined in the\n" 112 " schema. These fields will be discared when generating\n" 113 " binaries.\n" 114 " --no-prefix Don\'t prefix enum values with the enum type in C++.\n" 115 " --scoped-enums Use C++11 style scoped and strongly typed enums.\n" 116 " also implies --no-prefix.\n" 117 " --gen-includes (deprecated), this is the default behavior.\n" 118 " If the original behavior is required (no include\n" 119 " statements) use --no-includes.\n" 120 " --no-includes Don\'t generate include statements for included\n" 121 " schemas the generated file depends on (C++).\n" 122 " --gen-mutable Generate accessors that can mutate buffers in-place.\n" 123 " --gen-onefile Generate single output file for C#.\n" 124 " --gen-name-strings Generate type name functions for C++.\n" 125 " --escape-proto-ids Disable appending '_' in namespaces names.\n" 126 " --gen-object-api Generate an additional object-based API\n" 127 " --raw-binary Allow binaries without file_indentifier to be read.\n" 128 " This may crash flatc given a mismatched schema.\n" 129 " --proto Input is a .proto, translate to .fbs.\n" 130 " --schema Serialize schemas instead of JSON (use with -b)\n" 131 " --conform FILE Specify a schema the following schemas should be\n" 132 " an evolution of. Gives errors if not.\n" 133 "FILEs may be schemas, or JSON files (conforming to preceding schema)\n" 134 "FILEs after the -- must be binary flatbuffer format files.\n" 135 "Output files are named using the base file name of the input,\n" 136 "and written to the current directory or the path given by -o.\n" 137 "example: %s -c -b schema1.fbs schema2.fbs data.json\n", 138 g_program_name); 139 } 140 if (g_parser) delete g_parser; 141 exit(1); 142} 143 144static void ParseFile(flatbuffers::Parser &parser, const std::string &filename, 145 const std::string &contents, 146 std::vector<const char *> &include_directories) { 147 auto local_include_directory = flatbuffers::StripFileName(filename); 148 include_directories.push_back(local_include_directory.c_str()); 149 include_directories.push_back(nullptr); 150 if (!parser.Parse(contents.c_str(), &include_directories[0], 151 filename.c_str())) 152 Error(parser.error_, false, false); 153 include_directories.pop_back(); 154 include_directories.pop_back(); 155} 156 157int main(int argc, const char *argv[]) { 158 g_program_name = argv[0]; 159 flatbuffers::IDLOptions opts; 160 std::string output_path; 161 const size_t num_generators = sizeof(generators) / sizeof(generators[0]); 162 bool generator_enabled[num_generators] = { false }; 163 bool any_generator = false; 164 bool print_make_rules = false; 165 bool raw_binary = false; 166 bool schema_binary = false; 167 std::vector<std::string> filenames; 168 std::vector<const char *> include_directories; 169 size_t binary_files_from = std::numeric_limits<size_t>::max(); 170 std::string conform_to_schema; 171 for (int argi = 1; argi < argc; argi++) { 172 std::string arg = argv[argi]; 173 if (arg[0] == '-') { 174 if (filenames.size() && arg[1] != '-') 175 Error("invalid option location: " + arg, true); 176 if (arg == "-o") { 177 if (++argi >= argc) Error("missing path following: " + arg, true); 178 output_path = flatbuffers::ConCatPathFileName(argv[argi], ""); 179 } else if(arg == "-I") { 180 if (++argi >= argc) Error("missing path following" + arg, true); 181 include_directories.push_back(argv[argi]); 182 } else if(arg == "--conform") { 183 if (++argi >= argc) Error("missing path following" + arg, true); 184 conform_to_schema = argv[argi]; 185 } else if(arg == "--strict-json") { 186 opts.strict_json = true; 187 } else if(arg == "--no-js-exports") { 188 opts.skip_js_exports = true; 189 } else if(arg == "--defaults-json") { 190 opts.output_default_scalars_in_json = true; 191 } else if (arg == "--unknown-json") { 192 opts.skip_unexpected_fields_in_json = true; 193 } else if(arg == "--no-prefix") { 194 opts.prefixed_enums = false; 195 } else if(arg == "--scoped-enums") { 196 opts.prefixed_enums = false; 197 opts.scoped_enums = true; 198 } else if (arg == "--no-union-value-namespacing") { 199 opts.union_value_namespacing = false; 200 } else if(arg == "--gen-mutable") { 201 opts.mutable_buffer = true; 202 } else if(arg == "--gen-name-strings") { 203 opts.generate_name_strings = true; 204 } else if(arg == "--gen-object-api") { 205 opts.generate_object_based_api = true; 206 } else if(arg == "--gen-all") { 207 opts.generate_all = true; 208 opts.include_dependence_headers = false; 209 } else if(arg == "--gen-includes") { 210 // Deprecated, remove this option some time in the future. 211 printf("warning: --gen-includes is deprecated (it is now default)\n"); 212 } else if(arg == "--no-includes") { 213 opts.include_dependence_headers = false; 214 } else if (arg == "--gen-onefile") { 215 opts.one_file = true; 216 } else if (arg == "--raw-binary") { 217 raw_binary = true; 218 } else if(arg == "--") { // Separator between text and binary inputs. 219 binary_files_from = filenames.size(); 220 } else if(arg == "--proto") { 221 opts.proto_mode = true; 222 } else if(arg == "--escape-proto-ids") { 223 opts.escape_proto_identifiers = true; 224 } else if(arg == "--schema") { 225 schema_binary = true; 226 } else if(arg == "-M") { 227 print_make_rules = true; 228 } else if(arg == "--version") { 229 printf("flatc version %s\n", FLATC_VERSION); 230 exit(0); 231 } else { 232 for (size_t i = 0; i < num_generators; ++i) { 233 if (arg == generators[i].generator_opt_long || 234 (generators[i].generator_opt_short && 235 arg == generators[i].generator_opt_short)) { 236 generator_enabled[i] = true; 237 any_generator = true; 238 goto found; 239 } 240 } 241 Error("unknown commandline argument" + arg, true); 242 found:; 243 } 244 } else { 245 filenames.push_back(argv[argi]); 246 } 247 } 248 249 if (!filenames.size()) Error("missing input files", false, true); 250 251 if (opts.proto_mode) { 252 if (any_generator) 253 Error("cannot generate code directly from .proto files", true); 254 } else if (!any_generator && conform_to_schema.empty()) { 255 Error("no options: specify at least one generator.", true); 256 } 257 258 flatbuffers::Parser conform_parser; 259 if (!conform_to_schema.empty()) { 260 std::string contents; 261 if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) 262 Error("unable to load schema: " + conform_to_schema); 263 ParseFile(conform_parser, conform_to_schema, contents, include_directories); 264 } 265 266 // Now process the files: 267 g_parser = new flatbuffers::Parser(opts); 268 for (auto file_it = filenames.begin(); 269 file_it != filenames.end(); 270 ++file_it) { 271 std::string contents; 272 if (!flatbuffers::LoadFile(file_it->c_str(), true, &contents)) 273 Error("unable to load file: " + *file_it); 274 275 bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >= 276 binary_files_from; 277 if (is_binary) { 278 g_parser->builder_.Clear(); 279 g_parser->builder_.PushFlatBuffer( 280 reinterpret_cast<const uint8_t *>(contents.c_str()), 281 contents.length()); 282 if (!raw_binary) { 283 // Generally reading binaries that do not correspond to the schema 284 // will crash, and sadly there's no way around that when the binary 285 // does not contain a file identifier. 286 // We'd expect that typically any binary used as a file would have 287 // such an identifier, so by default we require them to match. 288 if (!g_parser->file_identifier_.length()) { 289 Error("current schema has no file_identifier: cannot test if \"" + 290 *file_it + 291 "\" matches the schema, use --raw-binary to read this file" 292 " anyway."); 293 } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), 294 g_parser->file_identifier_.c_str())) { 295 Error("binary \"" + 296 *file_it + 297 "\" does not have expected file_identifier \"" + 298 g_parser->file_identifier_ + 299 "\", use --raw-binary to read this file anyway."); 300 } 301 } 302 } else { 303 // Check if file contains 0 bytes. 304 if (contents.length() != strlen(contents.c_str())) { 305 Error("input file appears to be binary: " + *file_it, true); 306 } 307 auto is_schema = flatbuffers::GetExtension(*file_it) == "fbs"; 308 if (is_schema) { 309 // If we're processing multiple schemas, make sure to start each 310 // one from scratch. If it depends on previous schemas it must do 311 // so explicitly using an include. 312 delete g_parser; 313 g_parser = new flatbuffers::Parser(opts); 314 } 315 ParseFile(*g_parser, *file_it, contents, include_directories); 316 if (is_schema && !conform_to_schema.empty()) { 317 auto err = g_parser->ConformTo(conform_parser); 318 if (!err.empty()) Error("schemas don\'t conform: " + err); 319 } 320 if (schema_binary) { 321 g_parser->Serialize(); 322 g_parser->file_extension_ = reflection::SchemaExtension(); 323 } 324 } 325 326 std::string filebase = flatbuffers::StripPath( 327 flatbuffers::StripExtension(*file_it)); 328 329 for (size_t i = 0; i < num_generators; ++i) { 330 g_parser->opts.lang = generators[i].lang; 331 if (generator_enabled[i]) { 332 if (!print_make_rules) { 333 flatbuffers::EnsureDirExists(output_path); 334 if (!generators[i].generate(*g_parser, output_path, filebase)) { 335 Error(std::string("Unable to generate ") + 336 generators[i].lang_name + 337 " for " + 338 filebase); 339 } 340 } else { 341 std::string make_rule = generators[i].make_rule( 342 *g_parser, output_path, *file_it); 343 if (!make_rule.empty()) 344 printf("%s\n", flatbuffers::WordWrap( 345 make_rule, 80, " ", " \\").c_str()); 346 } 347 } 348 } 349 350 if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase); 351 352 // We do not want to generate code for the definitions in this file 353 // in any files coming up next. 354 g_parser->MarkGenerated(); 355 } 356 357 delete g_parser; 358 return 0; 359} 360