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