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