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