flatc.cpp revision d70f5ac6b02f250dca8e2c6e8f59a4223d1f66f6
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  { flatbuffers::GenerateGRPC, nullptr, "--grpc", "GRPC",
82    flatbuffers::IDLOptions::kMAX,
83    "Generate GRPC interfaces",
84    flatbuffers::CPPMakeRule },
85};
86
87const char *g_program_name = nullptr;
88flatbuffers::Parser *g_parser = nullptr;
89
90static void Error(const std::string &err, bool usage, bool show_exe_name) {
91  if (show_exe_name) printf("%s: ", g_program_name);
92  printf("%s\n", err.c_str());
93  if (usage) {
94    printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", g_program_name);
95    for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i)
96      printf("  %-12s %s %s.\n",
97             generators[i].generator_opt_long,
98             generators[i].generator_opt_short
99               ? generators[i].generator_opt_short
100               : "  ",
101             generators[i].generator_help);
102    printf(
103      "  -o PATH            Prefix PATH to all generated files.\n"
104      "  -I PATH            Search for includes in the specified path.\n"
105      "  -M                 Print make rules for generated files.\n"
106      "  --version          Print the version number of flatc and exit.\n"
107      "  --strict-json      Strict JSON: field names must be / will be quoted,\n"
108      "                     no trailing commas in tables/vectors.\n"
109      "  --defaults-json    Output fields whose value is the default when\n"
110      "                     writing JSON\n"
111      "  --unknown-json     Allow fields in JSON that are not defined in the\n"
112      "                     schema. These fields will be discared when generating\n"
113      "                     binaries.\n"
114      "  --no-prefix        Don\'t prefix enum values with the enum type in C++.\n"
115      "  --scoped-enums     Use C++11 style scoped and strongly typed enums.\n"
116      "                     also implies --no-prefix.\n"
117      "  --gen-includes     (deprecated), this is the default behavior.\n"
118      "                     If the original behavior is required (no include\n"
119      "                     statements) use --no-includes.\n"
120      "  --no-includes      Don\'t generate include statements for included\n"
121      "                     schemas the generated file depends on (C++).\n"
122      "  --gen-mutable      Generate accessors that can mutate buffers in-place.\n"
123      "  --gen-onefile      Generate single output file for C#.\n"
124      "  --gen-name-strings Generate type name functions for C++.\n"
125      "  --escape-proto-ids Disable appending '_' in namespaces names.\n"
126      "  --gen-object-api   Generate an additional object-based API\n"
127      "  --raw-binary       Allow binaries without file_indentifier to be read.\n"
128      "                     This may crash flatc given a mismatched schema.\n"
129      "  --proto            Input is a .proto, translate to .fbs.\n"
130      "  --schema           Serialize schemas instead of JSON (use with -b)\n"
131      "  --conform FILE     Specify a schema the following schemas should be\n"
132      "                     an evolution of. Gives errors if not.\n"
133      "FILEs may be schemas, or JSON files (conforming to preceding schema)\n"
134      "FILEs after the -- must be binary flatbuffer format files.\n"
135      "Output files are named using the base file name of the input,\n"
136      "and written to the current directory or the path given by -o.\n"
137      "example: %s -c -b schema1.fbs schema2.fbs data.json\n",
138      g_program_name);
139  }
140  if (g_parser) delete g_parser;
141  exit(1);
142}
143
144static void ParseFile(flatbuffers::Parser &parser, const std::string &filename,
145                      const std::string &contents,
146                      std::vector<const char *> &include_directories) {
147  auto local_include_directory = flatbuffers::StripFileName(filename);
148  include_directories.push_back(local_include_directory.c_str());
149  include_directories.push_back(nullptr);
150  if (!parser.Parse(contents.c_str(), &include_directories[0],
151                     filename.c_str()))
152    Error(parser.error_, false, false);
153  include_directories.pop_back();
154  include_directories.pop_back();
155}
156
157int main(int argc, const char *argv[]) {
158  g_program_name = argv[0];
159  flatbuffers::IDLOptions opts;
160  std::string output_path;
161  const size_t num_generators = sizeof(generators) / sizeof(generators[0]);
162  bool generator_enabled[num_generators] = { false };
163  bool any_generator = false;
164  bool print_make_rules = false;
165  bool raw_binary = false;
166  bool schema_binary = false;
167  std::vector<std::string> filenames;
168  std::vector<const char *> include_directories;
169  size_t binary_files_from = std::numeric_limits<size_t>::max();
170  std::string conform_to_schema;
171  for (int argi = 1; argi < argc; argi++) {
172    std::string arg = argv[argi];
173    if (arg[0] == '-') {
174      if (filenames.size() && arg[1] != '-')
175        Error("invalid option location: " + arg, true);
176      if (arg == "-o") {
177        if (++argi >= argc) Error("missing path following: " + arg, true);
178        output_path = flatbuffers::ConCatPathFileName(argv[argi], "");
179      } else if(arg == "-I") {
180        if (++argi >= argc) Error("missing path following" + arg, true);
181        include_directories.push_back(argv[argi]);
182      } else if(arg == "--conform") {
183        if (++argi >= argc) Error("missing path following" + arg, true);
184        conform_to_schema = argv[argi];
185      } else if(arg == "--strict-json") {
186        opts.strict_json = true;
187      } else if(arg == "--no-js-exports") {
188        opts.skip_js_exports = true;
189      } else if(arg == "--defaults-json") {
190        opts.output_default_scalars_in_json = true;
191      } else if (arg == "--unknown-json") {
192        opts.skip_unexpected_fields_in_json = true;
193      } else if(arg == "--no-prefix") {
194        opts.prefixed_enums = false;
195      } else if(arg == "--scoped-enums") {
196        opts.prefixed_enums = false;
197        opts.scoped_enums = true;
198      } else if (arg == "--no-union-value-namespacing") {
199        opts.union_value_namespacing = false;
200      } else if(arg == "--gen-mutable") {
201        opts.mutable_buffer = true;
202      } else if(arg == "--gen-name-strings") {
203        opts.generate_name_strings = true;
204      } else if(arg == "--gen-object-api") {
205        opts.generate_object_based_api = true;
206      } else if(arg == "--gen-all") {
207        opts.generate_all = true;
208        opts.include_dependence_headers = false;
209      } else if(arg == "--gen-includes") {
210        // Deprecated, remove this option some time in the future.
211        printf("warning: --gen-includes is deprecated (it is now default)\n");
212      } else if(arg == "--no-includes") {
213        opts.include_dependence_headers = false;
214      } else if (arg == "--gen-onefile") {
215        opts.one_file = true;
216      } else if (arg == "--raw-binary") {
217        raw_binary = true;
218      } else if(arg == "--") {  // Separator between text and binary inputs.
219        binary_files_from = filenames.size();
220      } else if(arg == "--proto") {
221        opts.proto_mode = true;
222      } else if(arg == "--escape-proto-ids") {
223        opts.escape_proto_identifiers = true;
224      } else if(arg == "--schema") {
225        schema_binary = true;
226      } else if(arg == "-M") {
227        print_make_rules = true;
228      } else if(arg == "--version") {
229        printf("flatc version %s\n", FLATC_VERSION);
230        exit(0);
231      } else {
232        for (size_t i = 0; i < num_generators; ++i) {
233          if (arg == generators[i].generator_opt_long ||
234              (generators[i].generator_opt_short &&
235               arg == generators[i].generator_opt_short)) {
236            generator_enabled[i] = true;
237            any_generator = true;
238            goto found;
239          }
240        }
241        Error("unknown commandline argument" + arg, true);
242        found:;
243      }
244    } else {
245      filenames.push_back(argv[argi]);
246    }
247  }
248
249  if (!filenames.size()) Error("missing input files", false, true);
250
251  if (opts.proto_mode) {
252    if (any_generator)
253      Error("cannot generate code directly from .proto files", true);
254  } else if (!any_generator && conform_to_schema.empty()) {
255    Error("no options: specify at least one generator.", true);
256  }
257
258  flatbuffers::Parser conform_parser;
259  if (!conform_to_schema.empty()) {
260    std::string contents;
261    if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents))
262      Error("unable to load schema: " + conform_to_schema);
263    ParseFile(conform_parser, conform_to_schema, contents, include_directories);
264  }
265
266  // Now process the files:
267  g_parser = new flatbuffers::Parser(opts);
268  for (auto file_it = filenames.begin();
269            file_it != filenames.end();
270          ++file_it) {
271      std::string contents;
272      if (!flatbuffers::LoadFile(file_it->c_str(), true, &contents))
273        Error("unable to load file: " + *file_it);
274
275      bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >=
276                       binary_files_from;
277      if (is_binary) {
278        g_parser->builder_.Clear();
279        g_parser->builder_.PushFlatBuffer(
280          reinterpret_cast<const uint8_t *>(contents.c_str()),
281          contents.length());
282        if (!raw_binary) {
283          // Generally reading binaries that do not correspond to the schema
284          // will crash, and sadly there's no way around that when the binary
285          // does not contain a file identifier.
286          // We'd expect that typically any binary used as a file would have
287          // such an identifier, so by default we require them to match.
288          if (!g_parser->file_identifier_.length()) {
289            Error("current schema has no file_identifier: cannot test if \"" +
290                 *file_it +
291                 "\" matches the schema, use --raw-binary to read this file"
292                 " anyway.");
293          } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(),
294                         g_parser->file_identifier_.c_str())) {
295            Error("binary \"" +
296                 *file_it +
297                 "\" does not have expected file_identifier \"" +
298                 g_parser->file_identifier_ +
299                 "\", use --raw-binary to read this file anyway.");
300          }
301        }
302      } else {
303        // Check if file contains 0 bytes.
304        if (contents.length() != strlen(contents.c_str())) {
305          Error("input file appears to be binary: " + *file_it, true);
306        }
307        auto is_schema = flatbuffers::GetExtension(*file_it) == "fbs";
308        if (is_schema) {
309          // If we're processing multiple schemas, make sure to start each
310          // one from scratch. If it depends on previous schemas it must do
311          // so explicitly using an include.
312          delete g_parser;
313          g_parser = new flatbuffers::Parser(opts);
314        }
315        ParseFile(*g_parser, *file_it, contents, include_directories);
316        if (is_schema && !conform_to_schema.empty()) {
317          auto err = g_parser->ConformTo(conform_parser);
318          if (!err.empty()) Error("schemas don\'t conform: " + err);
319        }
320        if (schema_binary) {
321          g_parser->Serialize();
322          g_parser->file_extension_ = reflection::SchemaExtension();
323        }
324      }
325
326      std::string filebase = flatbuffers::StripPath(
327                               flatbuffers::StripExtension(*file_it));
328
329      for (size_t i = 0; i < num_generators; ++i) {
330        g_parser->opts.lang = generators[i].lang;
331        if (generator_enabled[i]) {
332          if (!print_make_rules) {
333            flatbuffers::EnsureDirExists(output_path);
334            if (!generators[i].generate(*g_parser, output_path, filebase)) {
335              Error(std::string("Unable to generate ") +
336                    generators[i].lang_name +
337                    " for " +
338                    filebase);
339            }
340          } else {
341            std::string make_rule = generators[i].make_rule(
342                *g_parser, output_path, *file_it);
343            if (!make_rule.empty())
344              printf("%s\n", flatbuffers::WordWrap(
345                  make_rule, 80, " ", " \\").c_str());
346          }
347        }
348      }
349
350      if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase);
351
352      // We do not want to generate code for the definitions in this file
353      // in any files coming up next.
354      g_parser->MarkGenerated();
355  }
356
357  delete g_parser;
358  return 0;
359}
360