flatc.cpp revision a6a3f5925393146e879ba03d919a3380b9af7994
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.0 (" __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 " --no-js-exports Removes Node.js style export lines in JS.\n" 94 " --goog-js-export Uses goog.exports* for closure compiler exporting in JS.\n" 95 " --go-namespace Generate the overrided namespace in Golang.\n" 96 " --raw-binary Allow binaries without file_indentifier to be read.\n" 97 " This may crash flatc given a mismatched schema.\n" 98 " --proto Input is a .proto, translate to .fbs.\n" 99 " --grpc Generate GRPC interfaces for the specified languages\n" 100 " --schema Serialize schemas instead of JSON (use with -b)\n" 101 " --bfbs-comments Add doc comments to the binary schema files.\n" 102 " --conform FILE Specify a schema the following schemas should be\n" 103 " an evolution of. Gives errors if not.\n" 104 " --conform-includes Include path for the schema given with --conform\n" 105 " PATH \n" 106 " --include-prefix Prefix this path to any generated include statements.\n" 107 " PATH\n" 108 " --keep-prefix Keep original prefix of schema include statement.\n" 109 " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n" 110 " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n" 111 "FILEs may be schemas (must end in .fbs), or JSON files (conforming to preceding\n" 112 "schema). FILEs after the -- must be binary flatbuffer format files.\n" 113 "Output files are named using the base file name of the input,\n" 114 "and written to the current directory or the path given by -o.\n" 115 "example: " << program_name << " -c -b schema1.fbs schema2.fbs data.json\n"; 116 return ss.str(); 117} 118 119int FlatCompiler::Compile(int argc, const char** argv) { 120 if (params_.generators == nullptr || params_.num_generators == 0) { 121 return 0; 122 } 123 124 flatbuffers::IDLOptions opts; 125 std::string output_path; 126 127 bool any_generator = false; 128 bool print_make_rules = false; 129 bool raw_binary = false; 130 bool schema_binary = false; 131 bool grpc_enabled = false; 132 std::vector<std::string> filenames; 133 std::list<std::string> include_directories_storage; 134 std::vector<const char *> include_directories; 135 std::vector<const char *> conform_include_directories; 136 std::vector<bool> generator_enabled(params_.num_generators, false); 137 size_t binary_files_from = std::numeric_limits<size_t>::max(); 138 std::string conform_to_schema; 139 140 for (int argi = 0; argi < argc; argi++) { 141 std::string arg = argv[argi]; 142 if (arg[0] == '-') { 143 if (filenames.size() && arg[1] != '-') 144 Error("invalid option location: " + arg, true); 145 if (arg == "-o") { 146 if (++argi >= argc) Error("missing path following: " + arg, true); 147 output_path = flatbuffers::ConCatPathFileName( 148 flatbuffers::PosixPath(argv[argi]), ""); 149 } else if(arg == "-I") { 150 if (++argi >= argc) Error("missing path following" + arg, true); 151 include_directories_storage.push_back( 152 flatbuffers::PosixPath(argv[argi])); 153 include_directories.push_back( 154 include_directories_storage.back().c_str()); 155 } else if(arg == "--conform") { 156 if (++argi >= argc) Error("missing path following" + arg, true); 157 conform_to_schema = flatbuffers::PosixPath(argv[argi]); 158 } else if (arg == "--conform-includes") { 159 if (++argi >= argc) Error("missing path following" + arg, true); 160 include_directories_storage.push_back( 161 flatbuffers::PosixPath(argv[argi])); 162 conform_include_directories.push_back( 163 include_directories_storage.back().c_str()); 164 } else if (arg == "--include-prefix") { 165 if (++argi >= argc) Error("missing path following" + arg, true); 166 opts.include_prefix = flatbuffers::ConCatPathFileName( 167 flatbuffers::PosixPath(argv[argi]), ""); 168 } else if(arg == "--keep-prefix") { 169 opts.keep_include_path = true; 170 } else if(arg == "--strict-json") { 171 opts.strict_json = true; 172 } else if(arg == "--allow-non-utf8") { 173 opts.allow_non_utf8 = true; 174 } else if(arg == "--no-js-exports") { 175 opts.skip_js_exports = true; 176 } else if(arg == "--goog-js-export") { 177 opts.use_goog_js_export_format = true; 178 } else if(arg == "--go-namespace") { 179 if (++argi >= argc) Error("missing golang namespace" + arg, true); 180 opts.go_namespace = argv[argi]; 181 } else if(arg == "--defaults-json") { 182 opts.output_default_scalars_in_json = true; 183 } else if (arg == "--unknown-json") { 184 opts.skip_unexpected_fields_in_json = true; 185 } else if(arg == "--no-prefix") { 186 opts.prefixed_enums = false; 187 } else if(arg == "--scoped-enums") { 188 opts.prefixed_enums = false; 189 opts.scoped_enums = true; 190 } else if (arg == "--no-union-value-namespacing") { 191 opts.union_value_namespacing = false; 192 } else if(arg == "--gen-mutable") { 193 opts.mutable_buffer = true; 194 } else if(arg == "--gen-name-strings") { 195 opts.generate_name_strings = true; 196 } else if(arg == "--gen-object-api") { 197 opts.generate_object_based_api = true; 198 } else if (arg == "--cpp-ptr-type") { 199 if (++argi >= argc) Error("missing type following" + arg, true); 200 opts.cpp_object_api_pointer_type = argv[argi]; 201 } else if (arg == "--cpp-str-type") { 202 if (++argi >= argc) Error("missing type following" + arg, true); 203 opts.cpp_object_api_string_type = argv[argi]; 204 } else if(arg == "--gen-all") { 205 opts.generate_all = true; 206 opts.include_dependence_headers = false; 207 } else if(arg == "--gen-includes") { 208 // Deprecated, remove this option some time in the future. 209 printf("warning: --gen-includes is deprecated (it is now default)\n"); 210 } else if(arg == "--no-includes") { 211 opts.include_dependence_headers = false; 212 } else if (arg == "--gen-onefile") { 213 opts.one_file = true; 214 } else if (arg == "--raw-binary") { 215 raw_binary = true; 216 } else if(arg == "--") { // Separator between text and binary inputs. 217 binary_files_from = filenames.size(); 218 } else if(arg == "--proto") { 219 opts.proto_mode = true; 220 } else if(arg == "--escape-proto-ids") { 221 opts.escape_proto_identifiers = true; 222 } else if(arg == "--schema") { 223 schema_binary = true; 224 } else if(arg == "-M") { 225 print_make_rules = true; 226 } else if(arg == "--version") { 227 printf("flatc version %s\n", FLATC_VERSION); 228 exit(0); 229 } else if(arg == "--grpc") { 230 grpc_enabled = true; 231 } else if(arg == "--bfbs-comments") { 232 opts.binary_schema_comments = true; 233 } else if(arg == "--no-fb-import") { 234 opts.skip_flatbuffers_import = true; 235 } else if(arg == "--no-ts-reexport") { 236 opts.reexport_ts_modules = false; 237 } else { 238 for (size_t i = 0; i < params_.num_generators; ++i) { 239 if (arg == params_.generators[i].generator_opt_long || 240 (params_.generators[i].generator_opt_short && 241 arg == params_.generators[i].generator_opt_short)) { 242 generator_enabled[i] = true; 243 any_generator = true; 244 opts.lang_to_generate |= params_.generators[i].lang; 245 goto found; 246 } 247 } 248 Error("unknown commandline argument: " + arg, true); 249 found:; 250 } 251 } else { 252 filenames.push_back(flatbuffers::PosixPath(argv[argi])); 253 } 254 } 255 256 if (!filenames.size()) Error("missing input files", false, true); 257 258 if (opts.proto_mode) { 259 if (any_generator) 260 Error("cannot generate code directly from .proto files", true); 261 } else if (!any_generator && conform_to_schema.empty()) { 262 Error("no options: specify at least one generator.", true); 263 } 264 265 flatbuffers::Parser conform_parser; 266 if (!conform_to_schema.empty()) { 267 std::string contents; 268 if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) 269 Error("unable to load schema: " + conform_to_schema); 270 ParseFile(conform_parser, conform_to_schema, contents, 271 conform_include_directories); 272 } 273 274 std::unique_ptr<flatbuffers::Parser> parser(new flatbuffers::Parser(opts)); 275 276 for (auto file_it = filenames.begin(); 277 file_it != filenames.end(); 278 ++file_it) { 279 auto &filename = *file_it; 280 std::string contents; 281 if (!flatbuffers::LoadFile(filename.c_str(), true, &contents)) 282 Error("unable to load file: " + filename); 283 284 bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >= 285 binary_files_from; 286 auto is_schema = flatbuffers::GetExtension(filename) == "fbs"; 287 if (is_binary) { 288 parser->builder_.Clear(); 289 parser->builder_.PushFlatBuffer( 290 reinterpret_cast<const uint8_t *>(contents.c_str()), 291 contents.length()); 292 if (!raw_binary) { 293 // Generally reading binaries that do not correspond to the schema 294 // will crash, and sadly there's no way around that when the binary 295 // does not contain a file identifier. 296 // We'd expect that typically any binary used as a file would have 297 // such an identifier, so by default we require them to match. 298 if (!parser->file_identifier_.length()) { 299 Error("current schema has no file_identifier: cannot test if \"" + 300 filename + 301 "\" matches the schema, use --raw-binary to read this file" 302 " anyway."); 303 } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), 304 parser->file_identifier_.c_str())) { 305 Error("binary \"" + 306 filename + 307 "\" does not have expected file_identifier \"" + 308 parser->file_identifier_ + 309 "\", use --raw-binary to read this file anyway."); 310 } 311 } 312 } else { 313 // Check if file contains 0 bytes. 314 if (contents.length() != strlen(contents.c_str())) { 315 Error("input file appears to be binary: " + filename, true); 316 } 317 if (is_schema) { 318 // If we're processing multiple schemas, make sure to start each 319 // one from scratch. If it depends on previous schemas it must do 320 // so explicitly using an include. 321 parser.reset(new flatbuffers::Parser(opts)); 322 } 323 ParseFile(*parser.get(), filename, contents, include_directories); 324 if (!is_schema && !parser->builder_.GetSize()) { 325 // If a file doesn't end in .fbs, it must be json/binary. Ensure we 326 // didn't just parse a schema with a different extension. 327 Error("input file is neither json nor a .fbs (schema) file: " + 328 filename, true); 329 } 330 if (is_schema && !conform_to_schema.empty()) { 331 auto err = parser->ConformTo(conform_parser); 332 if (!err.empty()) Error("schemas don\'t conform: " + err); 333 } 334 if (schema_binary) { 335 parser->Serialize(); 336 parser->file_extension_ = reflection::SchemaExtension(); 337 } 338 } 339 340 std::string filebase = flatbuffers::StripPath( 341 flatbuffers::StripExtension(filename)); 342 343 for (size_t i = 0; i < params_.num_generators; ++i) { 344 parser->opts.lang = params_.generators[i].lang; 345 if (generator_enabled[i]) { 346 if (!print_make_rules) { 347 flatbuffers::EnsureDirExists(output_path); 348 if ((!params_.generators[i].schema_only || is_schema) && 349 !params_.generators[i].generate(*parser.get(), output_path, filebase)) { 350 Error(std::string("Unable to generate ") + 351 params_.generators[i].lang_name + 352 " for " + 353 filebase); 354 } 355 } else { 356 std::string make_rule = params_.generators[i].make_rule( 357 *parser.get(), output_path, filename); 358 if (!make_rule.empty()) 359 printf("%s\n", flatbuffers::WordWrap( 360 make_rule, 80, " ", " \\").c_str()); 361 } 362 if (grpc_enabled) { 363 if (params_.generators[i].generateGRPC != nullptr) { 364 if (!params_.generators[i].generateGRPC(*parser.get(), output_path, 365 filebase)) { 366 Error(std::string("Unable to generate GRPC interface for") + 367 params_.generators[i].lang_name); 368 } 369 } else { 370 Warn(std::string("GRPC interface generator not implemented for ") 371 + params_.generators[i].lang_name); 372 } 373 } 374 } 375 } 376 377 if (opts.proto_mode) GenerateFBS(*parser.get(), output_path, filebase); 378 379 // We do not want to generate code for the definitions in this file 380 // in any files coming up next. 381 parser->MarkGenerated(); 382 } 383 return 0; 384} 385 386} // namespace flatbuffers 387