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