2 * Copyright 2014 Google Inc. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "flatbuffers/flatc.h"
21 namespace flatbuffers {
23 const char *FLATC_VERSION() { return FLATBUFFERS_VERSION(); }
25 void FlatCompiler::ParseFile(
26 flatbuffers::Parser &parser, const std::string &filename,
27 const std::string &contents,
28 std::vector<const char *> &include_directories) const {
29 auto local_include_directory = flatbuffers::StripFileName(filename);
30 include_directories.push_back(local_include_directory.c_str());
31 include_directories.push_back(nullptr);
32 if (!parser.Parse(contents.c_str(), &include_directories[0],
34 Error(parser.error_, false, false);
36 if (!parser.error_.empty()) { Warn(parser.error_, false); }
37 include_directories.pop_back();
38 include_directories.pop_back();
41 void FlatCompiler::LoadBinarySchema(flatbuffers::Parser &parser,
42 const std::string &filename,
43 const std::string &contents) {
44 if (!parser.Deserialize(reinterpret_cast<const uint8_t *>(contents.c_str()),
46 Error("failed to load binary schema: " + filename, false, false);
50 void FlatCompiler::Warn(const std::string &warn, bool show_exe_name) const {
51 params_.warn_fn(this, warn, show_exe_name);
54 void FlatCompiler::Error(const std::string &err, bool usage,
55 bool show_exe_name) const {
56 params_.error_fn(this, err, usage, show_exe_name);
59 std::string FlatCompiler::GetUsageString(const char *program_name) const {
61 ss << "Usage: " << program_name << " [OPTION]... FILE... [-- FILE...]\n";
62 for (size_t i = 0; i < params_.num_generators; ++i) {
63 const Generator &g = params_.generators[i];
65 std::stringstream full_name;
66 full_name << std::setw(16) << std::left << g.generator_opt_long;
67 const char *name = g.generator_opt_short ? g.generator_opt_short : " ";
68 const char *help = g.generator_help;
70 ss << " " << full_name.str() << " " << name << " " << help << ".\n";
74 " -o PATH Prefix PATH to all generated files.\n"
75 " -I PATH Search for includes in the specified path.\n"
76 " -M Print make rules for generated files.\n"
77 " --version Print the version number of flatc and exit.\n"
78 " --strict-json Strict JSON: field names must be / will be quoted,\n"
79 " no trailing commas in tables/vectors.\n"
80 " --allow-non-utf8 Pass non-UTF-8 input through parser and emit nonstandard\n"
81 " \\x escapes in JSON. (Default is to raise parse error on\n"
82 " non-UTF-8 input.)\n"
83 " --natural-utf8 Output strings with UTF-8 as human-readable strings.\n"
84 " By default, UTF-8 characters are printed as \\uXXXX escapes.\n"
85 " --defaults-json Output fields whose value is the default when\n"
87 " --unknown-json Allow fields in JSON that are not defined in the\n"
88 " schema. These fields will be discared when generating\n"
90 " --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
91 " --scoped-enums Use C++11 style scoped and strongly typed enums.\n"
92 " also implies --no-prefix.\n"
93 " --gen-includes (deprecated), this is the default behavior.\n"
94 " If the original behavior is required (no include\n"
95 " statements) use --no-includes.\n"
96 " --no-includes Don\'t generate include statements for included\n"
97 " schemas the generated file depends on (C++ / Python).\n"
98 " --gen-mutable Generate accessors that can mutate buffers in-place.\n"
99 " --gen-onefile Generate single output file for C# and Go.\n"
100 " --gen-name-strings Generate type name functions for C++.\n"
101 " --gen-object-api Generate an additional object-based API.\n"
102 " --gen-compare Generate operator== for object-based API types.\n"
103 " --gen-nullable Add Clang _Nullable for C++ pointer. or @Nullable for Java\n"
104 " --java-checkerframe work Add @Pure for Java.\n"
105 " --gen-generated Add @Generated annotation for Java\n"
106 " --gen-all Generate not just code for the current schema files,\n"
107 " but for all files it includes as well.\n"
108 " If the language uses a single file for output (by default\n"
109 " the case for C++ and JS), all code will end up in this one\n"
111 " --cpp-include Adds an #include in generated file.\n"
112 " --cpp-ptr-type T Set object API pointer type (default std::unique_ptr).\n"
113 " --cpp-str-type T Set object API string type (default std::string).\n"
114 " T::c_str(), T::length() and T::empty() must be supported.\n"
115 " The custom type also needs to be constructible from std::string\n"
116 " (see the --cpp-str-flex-ctor option to change this behavior).\n"
117 " --cpp-str-flex-ctor Don't construct custom string types by passing std::string\n"
118 " from Flatbuffers, but (char* + length).\n"
119 " --cpp-std CPP_STD Generate a C++ code using features of selected C++ standard.\n"
120 " Supported CPP_STD values:\n"
121 " * 'c++0x' - generate code compatible with old compilers;\n"
122 " * 'c++11' - use C++11 code generator (default);\n"
123 " * 'c++17' - use C++17 features in generated code (experimental).\n"
124 " --object-prefix Customise class prefix for C++ object-based API.\n"
125 " --object-suffix Customise class suffix for C++ object-based API.\n"
126 " Default value is \"T\".\n"
127 " --no-js-exports Removes Node.js style export lines in JS.\n"
128 " --goog-js-export Uses goog.exports* for closure compiler exporting in JS.\n"
129 " --es6-js-export Uses ECMAScript 6 export style lines in JS.\n"
130 " --go-namespace Generate the overrided namespace in Golang.\n"
131 " --go-import Generate the overrided import for flatbuffers in Golang\n"
132 " (default is \"github.com/google/flatbuffers/go\").\n"
133 " --raw-binary Allow binaries without file_indentifier to be read.\n"
134 " This may crash flatc given a mismatched schema.\n"
135 " --size-prefixed Input binaries are size prefixed buffers.\n"
136 " --proto Input is a .proto, translate to .fbs.\n"
137 " --oneof-union Translate .proto oneofs to flatbuffer unions.\n"
138 " --grpc Generate GRPC interfaces for the specified languages.\n"
139 " --schema Serialize schemas instead of JSON (use with -b).\n"
140 " --bfbs-comments Add doc comments to the binary schema files.\n"
141 " --bfbs-builtins Add builtin attributes to the binary schema files.\n"
142 " --conform FILE Specify a schema the following schemas should be\n"
143 " an evolution of. Gives errors if not.\n"
144 " --conform-includes Include path for the schema given with --conform PATH\n"
145 " --include-prefix Prefix this path to any generated include statements.\n"
147 " --keep-prefix Keep original prefix of schema include statement.\n"
148 " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n"
149 " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n"
150 " --short-names Use short function names for JS and TypeScript.\n"
151 " --reflect-types Add minimal type reflection to code generation.\n"
152 " --reflect-names Add minimal type/name reflection.\n"
153 " --root-type T Select or override the default root_type\n"
154 " --force-defaults Emit default values in binary output from JSON\n"
155 " --force-empty When serializing from object API representation,\n"
156 " force strings and vectors to empty rather than null.\n"
157 " --force-empty-vectors When serializing from object API representation,\n"
158 " force vectors to empty rather than null.\n"
159 " --flexbuffers Used with \"binary\" and \"json\" options, it generates\n"
160 " data using schema-less FlexBuffers.\n"
161 "FILEs may be schemas (must end in .fbs), binary schemas (must end in .bfbs),\n"
162 "or JSON files (conforming to preceding schema). FILEs after the -- must be\n"
163 "binary flatbuffer format files.\n"
164 "Output files are named using the base file name of the input,\n"
165 "and written to the current directory or the path given by -o.\n"
166 "example: " << program_name << " -c -b schema1.fbs schema2.fbs data.json\n";
171 int FlatCompiler::Compile(int argc, const char **argv) {
172 if (params_.generators == nullptr || params_.num_generators == 0) {
176 flatbuffers::IDLOptions opts;
177 std::string output_path;
179 bool any_generator = false;
180 bool print_make_rules = false;
181 bool raw_binary = false;
182 bool schema_binary = false;
183 bool grpc_enabled = false;
184 std::vector<std::string> filenames;
185 std::list<std::string> include_directories_storage;
186 std::vector<const char *> include_directories;
187 std::vector<const char *> conform_include_directories;
188 std::vector<bool> generator_enabled(params_.num_generators, false);
189 size_t binary_files_from = std::numeric_limits<size_t>::max();
190 std::string conform_to_schema;
192 for (int argi = 0; argi < argc; argi++) {
193 std::string arg = argv[argi];
195 if (filenames.size() && arg[1] != '-')
196 Error("invalid option location: " + arg, true);
198 if (++argi >= argc) Error("missing path following: " + arg, true);
199 output_path = flatbuffers::ConCatPathFileName(
200 flatbuffers::PosixPath(argv[argi]), "");
201 } else if (arg == "-I") {
202 if (++argi >= argc) Error("missing path following" + arg, true);
203 include_directories_storage.push_back(
204 flatbuffers::PosixPath(argv[argi]));
205 include_directories.push_back(
206 include_directories_storage.back().c_str());
207 } else if (arg == "--conform") {
208 if (++argi >= argc) Error("missing path following" + arg, true);
209 conform_to_schema = flatbuffers::PosixPath(argv[argi]);
210 } else if (arg == "--conform-includes") {
211 if (++argi >= argc) Error("missing path following" + arg, true);
212 include_directories_storage.push_back(
213 flatbuffers::PosixPath(argv[argi]));
214 conform_include_directories.push_back(
215 include_directories_storage.back().c_str());
216 } else if (arg == "--include-prefix") {
217 if (++argi >= argc) Error("missing path following" + arg, true);
218 opts.include_prefix = flatbuffers::ConCatPathFileName(
219 flatbuffers::PosixPath(argv[argi]), "");
220 } else if (arg == "--keep-prefix") {
221 opts.keep_include_path = true;
222 } else if (arg == "--strict-json") {
223 opts.strict_json = true;
224 } else if (arg == "--allow-non-utf8") {
225 opts.allow_non_utf8 = true;
226 } else if (arg == "--natural-utf8") {
227 opts.natural_utf8 = true;
228 } else if (arg == "--no-js-exports") {
229 opts.skip_js_exports = true;
230 } else if (arg == "--goog-js-export") {
231 opts.use_goog_js_export_format = true;
232 opts.use_ES6_js_export_format = false;
233 } else if (arg == "--es6-js-export") {
234 opts.use_goog_js_export_format = false;
235 opts.use_ES6_js_export_format = true;
236 } else if (arg == "--go-namespace") {
237 if (++argi >= argc) Error("missing golang namespace" + arg, true);
238 opts.go_namespace = argv[argi];
239 } else if (arg == "--go-import") {
240 if (++argi >= argc) Error("missing golang import" + arg, true);
241 opts.go_import = argv[argi];
242 } else if (arg == "--defaults-json") {
243 opts.output_default_scalars_in_json = true;
244 } else if (arg == "--unknown-json") {
245 opts.skip_unexpected_fields_in_json = true;
246 } else if (arg == "--no-prefix") {
247 opts.prefixed_enums = false;
248 } else if (arg == "--scoped-enums") {
249 opts.prefixed_enums = false;
250 opts.scoped_enums = true;
251 } else if (arg == "--no-union-value-namespacing") {
252 opts.union_value_namespacing = false;
253 } else if (arg == "--gen-mutable") {
254 opts.mutable_buffer = true;
255 } else if (arg == "--gen-name-strings") {
256 opts.generate_name_strings = true;
257 } else if (arg == "--gen-object-api") {
258 opts.generate_object_based_api = true;
259 } else if (arg == "--gen-compare") {
260 opts.gen_compare = true;
261 } else if (arg == "--cpp-include") {
262 if (++argi >= argc) Error("missing include following" + arg, true);
263 opts.cpp_includes.push_back(argv[argi]);
264 } else if (arg == "--cpp-ptr-type") {
265 if (++argi >= argc) Error("missing type following" + arg, true);
266 opts.cpp_object_api_pointer_type = argv[argi];
267 } else if (arg == "--cpp-str-type") {
268 if (++argi >= argc) Error("missing type following" + arg, true);
269 opts.cpp_object_api_string_type = argv[argi];
270 } else if (arg == "--cpp-str-flex-ctor") {
271 opts.cpp_object_api_string_flexible_constructor = true;
272 } else if (arg == "--gen-nullable") {
273 opts.gen_nullable = true;
274 } else if (arg == "--java-checkerframework") {
275 opts.java_checkerframework = true;
276 } else if (arg == "--gen-generated") {
277 opts.gen_generated = true;
278 } else if (arg == "--object-prefix") {
279 if (++argi >= argc) Error("missing prefix following" + arg, true);
280 opts.object_prefix = argv[argi];
281 } else if (arg == "--object-suffix") {
282 if (++argi >= argc) Error("missing suffix following" + arg, true);
283 opts.object_suffix = argv[argi];
284 } else if (arg == "--gen-all") {
285 opts.generate_all = true;
286 opts.include_dependence_headers = false;
287 } else if (arg == "--gen-includes") {
288 // Deprecated, remove this option some time in the future.
289 Warn("warning: --gen-includes is deprecated (it is now default)\n");
290 } else if (arg == "--no-includes") {
291 opts.include_dependence_headers = false;
292 } else if (arg == "--gen-onefile") {
293 opts.one_file = true;
294 } else if (arg == "--raw-binary") {
296 } else if (arg == "--size-prefixed") {
297 opts.size_prefixed = true;
298 } else if (arg == "--") { // Separator between text and binary inputs.
299 binary_files_from = filenames.size();
300 } else if (arg == "--proto") {
301 opts.proto_mode = true;
302 } else if (arg == "--oneof-union") {
303 opts.proto_oneof_union = true;
304 } else if (arg == "--schema") {
305 schema_binary = true;
306 } else if (arg == "-M") {
307 print_make_rules = true;
308 } else if (arg == "--version") {
309 printf("flatc version %s\n", FLATC_VERSION());
311 } else if (arg == "--grpc") {
313 } else if (arg == "--bfbs-comments") {
314 opts.binary_schema_comments = true;
315 } else if (arg == "--bfbs-builtins") {
316 opts.binary_schema_builtins = true;
317 } else if (arg == "--no-fb-import") {
318 opts.skip_flatbuffers_import = true;
319 } else if (arg == "--no-ts-reexport") {
320 opts.reexport_ts_modules = false;
321 } else if (arg == "--short-names") {
322 opts.js_ts_short_names = true;
323 } else if (arg == "--reflect-types") {
324 opts.mini_reflect = IDLOptions::kTypes;
325 } else if (arg == "--reflect-names") {
326 opts.mini_reflect = IDLOptions::kTypesAndNames;
327 } else if (arg == "--root-type") {
328 if (++argi >= argc) Error("missing type following" + arg, true);
329 opts.root_type = argv[argi];
330 } else if (arg == "--force-defaults") {
331 opts.force_defaults = true;
332 } else if (arg == "--force-empty") {
333 opts.set_empty_strings_to_null = false;
334 opts.set_empty_vectors_to_null = false;
335 } else if (arg == "--force-empty-vectors") {
336 opts.set_empty_vectors_to_null = false;
337 } else if (arg == "--java-primitive-has-method") {
338 opts.java_primitive_has_method = true;
339 } else if (arg == "--flexbuffers") {
340 opts.use_flexbuffers = true;
341 } else if(arg == "--cpp-std") {
342 if (++argi >= argc) Error("missing C++ standard specification" + arg, true);
343 opts.cpp_std = argv[argi];
345 for (size_t i = 0; i < params_.num_generators; ++i) {
346 if (arg == params_.generators[i].generator_opt_long ||
347 (params_.generators[i].generator_opt_short &&
348 arg == params_.generators[i].generator_opt_short)) {
349 generator_enabled[i] = true;
350 any_generator = true;
351 opts.lang_to_generate |= params_.generators[i].lang;
355 Error("unknown commandline argument: " + arg, true);
359 filenames.push_back(flatbuffers::PosixPath(argv[argi]));
363 if (!filenames.size()) Error("missing input files", false, true);
365 if (opts.proto_mode) {
367 Error("cannot generate code directly from .proto files", true);
368 } else if (!any_generator && conform_to_schema.empty()) {
369 Error("no options: specify at least one generator.", true);
372 flatbuffers::Parser conform_parser;
373 if (!conform_to_schema.empty()) {
374 std::string contents;
375 if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents))
376 Error("unable to load schema: " + conform_to_schema);
378 if (flatbuffers::GetExtension(conform_to_schema) ==
379 reflection::SchemaExtension()) {
380 LoadBinarySchema(conform_parser, conform_to_schema, contents);
382 ParseFile(conform_parser, conform_to_schema, contents,
383 conform_include_directories);
387 std::unique_ptr<flatbuffers::Parser> parser(new flatbuffers::Parser(opts));
389 for (auto file_it = filenames.begin(); file_it != filenames.end();
391 auto &filename = *file_it;
392 std::string contents;
393 if (!flatbuffers::LoadFile(filename.c_str(), true, &contents))
394 Error("unable to load file: " + filename);
397 static_cast<size_t>(file_it - filenames.begin()) >= binary_files_from;
398 auto ext = flatbuffers::GetExtension(filename);
399 auto is_schema = ext == "fbs" || ext == "proto";
400 auto is_binary_schema = ext == reflection::SchemaExtension();
402 parser->builder_.Clear();
403 parser->builder_.PushFlatBuffer(
404 reinterpret_cast<const uint8_t *>(contents.c_str()),
407 // Generally reading binaries that do not correspond to the schema
408 // will crash, and sadly there's no way around that when the binary
409 // does not contain a file identifier.
410 // We'd expect that typically any binary used as a file would have
411 // such an identifier, so by default we require them to match.
412 if (!parser->file_identifier_.length()) {
413 Error("current schema has no file_identifier: cannot test if \"" +
415 "\" matches the schema, use --raw-binary to read this file"
417 } else if (!flatbuffers::BufferHasIdentifier(
418 contents.c_str(), parser->file_identifier_.c_str(),
419 opts.size_prefixed)) {
420 Error("binary \"" + filename +
421 "\" does not have expected file_identifier \"" +
422 parser->file_identifier_ +
423 "\", use --raw-binary to read this file anyway.");
427 // Check if file contains 0 bytes.
428 if (!opts.use_flexbuffers && !is_binary_schema &&
429 contents.length() != strlen(contents.c_str())) {
430 Error("input file appears to be binary: " + filename, true);
433 // If we're processing multiple schemas, make sure to start each
434 // one from scratch. If it depends on previous schemas it must do
435 // so explicitly using an include.
436 parser.reset(new flatbuffers::Parser(opts));
438 if (is_binary_schema) {
439 LoadBinarySchema(*parser.get(), filename, contents);
441 if (opts.use_flexbuffers) {
442 if (opts.lang_to_generate == IDLOptions::kJson) {
443 parser->flex_root_ = flexbuffers::GetRoot(
444 reinterpret_cast<const uint8_t *>(contents.c_str()),
447 parser->flex_builder_.Clear();
448 ParseFile(*parser.get(), filename, contents, include_directories);
451 ParseFile(*parser.get(), filename, contents, include_directories);
452 if (!is_schema && !parser->builder_.GetSize()) {
453 // If a file doesn't end in .fbs, it must be json/binary. Ensure we
454 // didn't just parse a schema with a different extension.
455 Error("input file is neither json nor a .fbs (schema) file: " +
460 if ((is_schema || is_binary_schema) && !conform_to_schema.empty()) {
461 auto err = parser->ConformTo(conform_parser);
462 if (!err.empty()) Error("schemas don\'t conform: " + err);
466 parser->file_extension_ = reflection::SchemaExtension();
470 std::string filebase =
471 flatbuffers::StripPath(flatbuffers::StripExtension(filename));
473 for (size_t i = 0; i < params_.num_generators; ++i) {
474 parser->opts.lang = params_.generators[i].lang;
475 if (generator_enabled[i]) {
476 if (!print_make_rules) {
477 flatbuffers::EnsureDirExists(output_path);
478 if ((!params_.generators[i].schema_only ||
479 (is_schema || is_binary_schema)) &&
480 !params_.generators[i].generate(*parser.get(), output_path,
482 Error(std::string("Unable to generate ") +
483 params_.generators[i].lang_name + " for " + filebase);
486 if (params_.generators[i].make_rule == nullptr) {
487 Error(std::string("Cannot generate make rule for ") +
488 params_.generators[i].lang_name);
490 std::string make_rule = params_.generators[i].make_rule(
491 *parser.get(), output_path, filename);
492 if (!make_rule.empty())
494 flatbuffers::WordWrap(make_rule, 80, " ", " \\").c_str());
498 if (params_.generators[i].generateGRPC != nullptr) {
499 if (!params_.generators[i].generateGRPC(*parser.get(), output_path,
501 Error(std::string("Unable to generate GRPC interface for") +
502 params_.generators[i].lang_name);
505 Warn(std::string("GRPC interface generator not implemented for ") +
506 params_.generators[i].lang_name);
512 if (!opts.root_type.empty()) {
513 if (!parser->SetRootType(opts.root_type.c_str()))
514 Error("unknown root type: " + opts.root_type);
515 else if (parser->root_struct_def_->fixed)
516 Error("root type must be a table");
519 if (opts.proto_mode) GenerateFBS(*parser.get(), output_path, filebase);
521 // We do not want to generate code for the definitions in this file
522 // in any files coming up next.
523 parser->MarkGenerated();
528 } // namespace flatbuffers