Home | History | Annotate | Download | only in src
      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 
     23 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 
     38 void FlatCompiler::Warn(const std::string &warn, bool show_exe_name) const {
     39   params_.warn_fn(this, warn, show_exe_name);
     40 }
     41 
     42 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 
     47 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 
    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