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