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