diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
commit | 5a40295c4cf0af5ea8da9ced04a4ce7d3621a080 (patch) | |
tree | cb21506e7b04d10b45d6066a0ee1655563d5d52b /src/cli/flatcc_cli.c |
Squashed 'flatcc/' content from commit 473da2a
git-subtree-dir: flatcc
git-subtree-split: 473da2afa5ca435363f8c5e6569167aee6bc31c5
Diffstat (limited to 'src/cli/flatcc_cli.c')
-rw-r--r-- | src/cli/flatcc_cli.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/src/cli/flatcc_cli.c b/src/cli/flatcc_cli.c new file mode 100644 index 0000000..9a03dec --- /dev/null +++ b/src/cli/flatcc_cli.c @@ -0,0 +1,505 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "flatcc/flatcc.h" +#include "config.h" + +#define VERSION FLATCC_VERSION_TEXT +#define TITLE FLATCC_TITLE_TEXT + +void usage(FILE *fp) +{ + fprintf(fp, "%s\n", TITLE); + fprintf(fp, "version: %s\n", VERSION); + fprintf(fp, "usage: flatcc [options] file [...]\n"); + fprintf(fp, "options:\n" + " --reader (default) Generate reader\n" + " -c, --common Generate common include header(s)\n" + " --common_reader Generate common reader include header(s)\n" + " --common_builder Generate common builder include header(s)\n" + " -w, --builder Generate builders (writable buffers)\n" + " -v, --verifier Generate verifier\n" + " -r, --recursive Recursively generate included schema files\n" + " -a Generate all (like -cwvr)\n" + " -g Use _get suffix only to avoid conflicts\n" + " -d Dependency file like gcc -MMD\n" + " -I<inpath> Search path for include files (multiple allowed)\n" + " -o<outpath> Write files relative to this path (dir must exist)\n" + " --stdout Concatenate all output to stdout\n" + " --outfile=<file> Like --stdout, but to a file.\n" + " --depfile=<file> Dependency file like gcc -MF.\n" + " --deptarget=<file> Override --depfile target like gcc -MT.\n" + " --prefix=<prefix> Add prefix to all generated names (no _ added)\n" + " --common-prefix=<prefix> Replace 'flatbuffers' prefix in common files\n" +#if FLATCC_REFLECTION + " --schema Generate binary schema (.bfbs)\n" + " --schema-length=no Add length prefix to binary schema\n" +#endif + " --verifier Generate verifier for schema\n" + " --json-parser Generate json parser for schema\n" + " --json-printer Generate json printer for schema\n" + " --json Generate both json parser and printer for schema\n" + " --version Show version\n" + " -h | --help Help message\n" + ); +} + +void help(FILE *fp) +{ + usage(fp); + fprintf(fp, + "\n" + "This is a flatbuffer compatible compiler implemented in C generating C\n" + "source. It is largely compatible with the flatc compiler provided by\n" + "Google Fun Propulsion Lab but does not support JSON objects or binary\n" + "schema.\n" + "\n" + "By example 'flatcc monster.fbs' generates a 'monster.h' file which\n" + "provides functions to read a flatbuffer. A common include header is also\n" + "required. The common file is generated with the -c option. The reader\n" + "has no external dependencies.\n" + "\n" + "The -w (--builder) option enables code generation to build buffers:\n" + "`flatbuffers -w monster.fbs` will generate `monster.h` and\n" + "`monster_builder.h`, and also a builder specific common file with the\n" + "-cw option. The builder must link with the extern `flatbuilder` library.\n" + "\n" + "-v (--verifier) generates a verifier file per schema. It depends on the\n" + "runtime library but not on other generated files, except other included\n" + "verifiers.\n" + "\n" + "-r (--recursive) generates all schema included recursively.\n" + "\n" + "--reader is the default option to generate reader output but can be used\n" + "explicitly together with other options that would otherwise disable it.\n" + "\n" + "All C output can be concated to a single file using --stdout or\n" + "--outfile with content produced in dependency order. The outfile is\n" + "relative to cwd.\n" + "\n" + "-g Only add '_get' suffix to read accessors such that, for example,\n" + "only 'Monster_name_get(monster)` will be generated and not also\n" + "'Monster_name(monster)'. This avoids potential conflicts with\n" + "other generated symbols when a schema change is impractical.\n" + "\n" + "-d generates a dependency file, e.g. 'monster.fbs.d' in the output dir.\n" + "\n" + "--depfile implies -d but accepts an explicit filename with a path\n" + "relative to cwd. The dependency files content is a gnu make rule with a\n" + "target followed by the included schema files The target must match how\n" + "it is seen by the rest of the build system and defaults to e.g.\n" + "'monster_reader.h' or 'monster.bfbs' paths relative to the working\n" + "directory.\n" + "\n" + "--deptarget overrides the default target for --depfile, simiar to gcc -MT.\n" + "\n" + +#if FLATCC_REFLECTION + "--schema will generate a binary .bfbs file for each top-level schema file.\n" + "Can be used with --stdout if no C output is specified. When used with multiple\n" + "files --schema-length=yes is recommend.\n" + "\n" + "--schema-length adds a length prefix of type uoffset_t to binary schema so\n" + "they can be concatenated - the aligned buffer starts after the prefix.\n" + "\n" +#else + "Flatbuffers binary schema support (--schema) has been disabled." + "\n" +#endif + "--json-parser generates a file that implements a fast typed json parser for\n" + "the schema. It depends on some flatcc headers and the runtime library but\n" + "not on other generated files except other parsers from included schema.\n" + "\n" + "--json-printer generates a file that implements json printers for the schema\n" + "and has dependencies similar to --json-parser.\n" + "\n" + "--json is generates both printer and parser.\n" + "\n" +#if FLATCC_REFLECTION +#if 0 /* Disable deprecated features. */ + "DEPRECATED:\n" + " --schema-namespace controls if typenames in schema are prefixed a namespace.\n" + " namespaces should always be present.\n" + "\n" +#endif +#endif + "The generated source can redefine offset sizes by including a modified\n" + "`flatcc_types.h` file. The flatbuilder library must then be compiled with the\n" + "same `flatcc_types.h` file. In this case --prefix and --common-prefix options\n" + "may be helpful to avoid conflict with standard offset sizes.\n" + "\n" + "The output size may seem bulky, but most content is rarely used inline\n" + "functions and macros. The compiled binary need not be large.\n" + "\n" + "The generated source assumes C11 functionality for alignment, compile\n" + "time assertions and inline functions but an optional set of portability\n" + "headers can be included to work with most any compiler. The portability\n" + "layer is not throughly tested so a platform specific test is required\n" + "before production use. Upstream patches are welcome.\n"); +} + +enum { noarg, suffixarg, nextarg }; + +int parse_bool_arg(const char *a) +{ + if (strcmp(a, "0") == 0 || strcmp(a, "no") == 0) { + return 0; + } + if (strcmp(a, "1") == 0 || strcmp(a, "yes") == 0) { + return 1; + } + fprintf(stderr, "invalid boolean argument: '%s', must be '0', '1', 'yes' or 'no'\n", a); + return -1; +} + +int match_long_arg(const char *option, const char *s, size_t n) +{ + return strncmp(option, s, n) == 0 && strlen(option) == n; +} + +int set_opt(flatcc_options_t *opts, const char *s, const char *a) +{ + int ret = noarg; + size_t n = strlen(s); + const char *v = strchr(s, '='); + if (v) { + a = v + 1; + n = (size_t)(v - s); + } + if (*s == 'h' || 0 == strcmp("-help", s)) { + /* stdout so less and more works. */ + help(stdout); + exit(0); + } + if (0 == strcmp("-version", s)) { + fprintf(stdout, "%s\n", TITLE); + fprintf(stdout, "version: %s\n", VERSION); + exit(0); + } + if (0 == strcmp("-stdout", s)) { + opts->gen_stdout = 1; + return noarg; + } + if (0 == strcmp("-common", s)) { + opts->cgen_common_reader = 1; + opts->cgen_common_builder = 1; + return noarg; + } + if (0 == strcmp("-common_reader", s)) { + opts->cgen_common_reader = 1; + return noarg; + } + if (0 == strcmp("-common_builder", s)) { + opts->cgen_common_builder = 1; + return noarg; + } + if (0 == strcmp("-reader", s)) { + opts->cgen_reader = 1; + return noarg; + } + if (0 == strcmp("-builder", s)) { + opts->cgen_builder = 1; + return noarg; + } + if (0 == strcmp("-verifier", s)) { + opts->cgen_verifier = 1; + return noarg; + } + if (0 == strcmp("-recursive", s)) { + opts->cgen_recursive = 1; + return noarg; + } +#if FLATCC_REFLECTION + if (0 == strcmp("-schema", s)) { + opts->bgen_bfbs = 1; + return noarg; + } +#endif + if (0 == strcmp("-json-parser", s)) { + opts->cgen_json_parser = 1; + return noarg; + } + if (0 == strcmp("-json-printer", s)) { + opts->cgen_json_printer = 1; + return noarg; + } + if (0 == strcmp("-json", s)) { + opts->cgen_json_parser = 1; + opts->cgen_json_printer = 1; + return noarg; + } +#if FLATCC_REFLECTION +#if 0 /* Disable deprecated features. */ + if (match_long_arg("-schema-namespace", s, n)) { + fprintf(stderr, "warning: --schema-namespace is deprecated\n" + " a namespace is added by default and should always be present\n"); + if (!a) { + fprintf(stderr, "--schema-namespace option needs an argument\n"); + exit(-1); + } + if(0 > (opts->bgen_qualify_names = parse_bool_arg(a))) { + exit(-1); + } + return v ? noarg : nextarg; + } + if (match_long_arg("-schema-length", s, n)) { + if (!a) { + fprintf(stderr, "--schema-length option needs an argument\n"); + exit(-1); + } + if(0 > (opts->bgen_length_prefix = parse_bool_arg(a))) { + exit(-1); + } + return v ? noarg : nextarg; + } +#endif +#endif + if (match_long_arg("-depfile", s, n)) { + if (!a) { + fprintf(stderr, "--depfile option needs an argument\n"); + exit(-1); + } + opts->gen_depfile = a; + opts->gen_dep = 1; + return v ? noarg : nextarg; + } + if (match_long_arg("-deptarget", s, n)) { + if (!a) { + fprintf(stderr, "--deptarget option needs an argument\n"); + exit(-1); + } + opts->gen_deptarget = a; + return v ? noarg : nextarg; + } + if (match_long_arg("-outfile", s, n)) { + if (!a) { + fprintf(stderr, "--outfile option needs an argument\n"); + exit(-1); + } + opts->gen_outfile= a; + return v ? noarg : nextarg; + } + if (match_long_arg("-common-prefix", s, n)) { + if (!a) { + fprintf(stderr, "--common-prefix option needs an argument\n"); + exit(-1); + } + opts->nsc = a; + return v ? noarg : nextarg; + } + if (match_long_arg("-prefix", s, n)) { + if (!a) { + fprintf(stderr, "-n option needs an argument\n"); + exit(-1); + } + opts->ns = a; + return v ? noarg : nextarg; + } + switch (*s) { + case '-': + fprintf(stderr, "invalid option: -%s\n", s); + exit(-1); + case 'I': + if (s[1]) { + ret = suffixarg; + a = s + 1; + } else if (!a) { + fprintf(stderr, "-I option needs an argument\n"); + exit(-1); + } else { + ret = nextarg; + } + opts->inpaths[opts->inpath_count++] = a; + return ret; + case 'o': + if (opts->outpath) { + fprintf(stderr, "-o option can only be specified once\n"); + exit(-1); + } + if (s[1]) { + ret = suffixarg; + a = s + 1; + } else if (!a) { + fprintf(stderr, "-o option needs an argument\n"); + exit(-1); + } else { + ret = nextarg; + } + opts->outpath = a; + return ret; + case 'w': + opts->cgen_builder = 1; + return noarg; + case 'v': + opts->cgen_verifier = 1; + return noarg; + case 'c': + opts->cgen_common_reader = 1; + opts->cgen_common_builder = 1; + return noarg; + case 'r': + opts->cgen_recursive = 1; + return noarg; + case 'g': + opts->cgen_no_conflicts = 1; + return noarg; + case 'd': + opts->gen_dep = 1; + return noarg; + case 'a': + opts->cgen_reader = 1; + opts->cgen_builder = 1; + opts->cgen_verifier = 1; + opts->cgen_common_reader = 1; + opts->cgen_common_builder = 1; + opts->cgen_recursive = 1; + return noarg; + default: + fprintf(stderr, "invalid option: -%c\n", *s); + exit(-1); + } + return noarg; +} + +int get_opt(flatcc_options_t *opts, const char *s, const char *a) +{ + if (s[1] == '-') { + return nextarg == set_opt(opts, s + 1, a); + } + ++s; + if (*s == 0) { + fprintf(stderr, "- is not a valid option\n"); + exit(-1); + } + while (*s) { + switch (set_opt(opts, s, a)) { + case noarg: + ++s; + continue; + case suffixarg: + return 0; + case nextarg: + return 1; + } + } + return noarg; +} + +void parse_opts(int argc, const char *argv[], flatcc_options_t *opts) +{ + int i; + const char *s, *a; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + s = argv[i]; + a = i + 1 < argc ? argv[i + 1] : 0; + i += get_opt(opts, s, a); + } else { + opts->srcpaths[opts->srcpath_count++] = argv[i]; + } + } +} + +int main(int argc, const char *argv[]) +{ + flatcc_options_t opts; + flatcc_context_t ctx = 0; + int i, ret, cgen; + const char **src; + + ctx = 0; + ret = 0; + if (argc < 2) { + usage(stderr); + exit(-1); + } + flatcc_init_options(&opts); + if (!(opts.inpaths = malloc((size_t)argc * sizeof(char *)))) { + fprintf(stderr, "memory allocation failure\n"); + exit(-1); + } + if (!(opts.srcpaths = malloc((size_t)argc * sizeof(char *)))) { + fprintf(stderr, "memory allocation failure\n"); + free((void *)opts.inpaths); + exit(-1); + } + + parse_opts(argc, argv, &opts); + if (opts.cgen_builder && opts.cgen_common_reader) { + opts.cgen_common_builder = 1; + } + if (opts.srcpath_count == 0) { + /* No input files, so only generate header(s). */ + if (!(opts.cgen_common_reader || opts.cgen_common_builder) || opts.bgen_bfbs) { + fprintf(stderr, "filename missing\n"); + goto fail; + } + if (!(ctx = flatcc_create_context(&opts, 0, 0, 0))) { + fprintf(stderr, "internal error: failed to create parsing context\n"); + goto fail; + } + if (flatcc_generate_files(ctx)) { + goto fail; + } + flatcc_destroy_context(ctx); + ctx = 0; + goto done; + } + cgen = opts.cgen_reader || opts.cgen_builder || opts.cgen_verifier + || opts.cgen_common_reader || opts.cgen_common_builder + || opts.cgen_json_parser || opts.cgen_json_printer; + if (!opts.bgen_bfbs && (!cgen || opts.cgen_builder || opts.cgen_verifier)) { + /* Assume default if no other output specified when deps required it. */ + opts.cgen_reader = 1; + } + if (opts.bgen_bfbs && cgen) { + if (opts.gen_stdout) { + fprintf(stderr, "--stdout cannot be used with mixed text and binary output"); + goto fail; + } + if (opts.gen_outfile) { + fprintf(stderr, "--outfile cannot be used with mixed text and binary output"); + goto fail; + } + } + if (opts.gen_deptarget && !opts.gen_depfile) { + fprintf(stderr, "--deptarget cannot be used without --depfile"); + goto fail; + } + if (opts.gen_stdout && opts.gen_outfile) { + fprintf(stderr, "--outfile cannot be used with --stdout"); + goto fail; + } + for (i = 0, src = opts.srcpaths; i < opts.srcpath_count; ++i, ++src) { + if (!(ctx = flatcc_create_context(&opts, *src, 0, 0))) { + fprintf(stderr, "internal error: failed to create parsing context\n"); + goto fail; + } + if (flatcc_parse_file(ctx, *src)) { + goto fail; + } + if (flatcc_generate_files(ctx)) { + goto fail; + } + flatcc_destroy_context(ctx); + ctx = 0; + /* for --stdout and --outfile options: append to file and skip generating common headers. */ + opts.gen_append = 1; + } + goto done; +fail: + ret = -1; +done: + if (ctx) { + flatcc_destroy_context(ctx); + ctx = 0; + } + if (ret) { + fprintf(stderr, "output failed\n"); + } + free((void *)opts.inpaths); + free((void *)opts.srcpaths); + return ret; +} |